how do I access the main class's stage? + Can I pass functions as arguments like this? - actionscript-3

There are two files in my actionscript project named "TestAPP", TestAPP.as and Draggable.as
TestAPP.as:
package {
import flash.display.Sprite;
import flash.display.Stage;
public class TestAPP extends Sprite
{
var _mainStage:Stage;
public function TestAPP()//This is where we test the UI components.
{
var sp:Sprite = new Sprite();
_mainStage = stage;
_mainStage.addChild(sp);
sp.graphics.beginFill(0x00FF00);
sp.graphics.drawCircle(0,0,10);
sp.graphics.endFill();
sp.x = 50;
sp.y = 50;
var draggable1:Draggable = new draggable(sp,_mainStage,limitingfunc);
}
public function limitingfunc(x:Number,y:Number):int{
return 0;
}
}
}
And for the draggable.as:
package
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
public class Draggable
{
private var _limitingFunc:Function;
private var _which:Sprite;
private var _MouseSavedX:Number;
private var _MouseSavedY:Number;
private var _stage:Stage;
public function Draggable(which:Sprite,stage:Stage,limitingfunc:Function)
{
_limitingFunc = limitingfunc;
_which = which;
_stage = stage;
_which.addEventListener(MouseEvent.MOUSE_DOWN,begin_drag);
}
//limiting func: returns 0 when the object is free to move that place.
//returns -1 when the user wants to block X coordinate changes (but maintain Y free)
//returns -2 when the user wants to block Y ...
//returns -3 or less when the user wants to block both X and Y from changing.
//returns
private function Return_0(x:Number = 0,y:Number = 0):int{
return 0;
}
private function begin_drag(ev:MouseEvent):void{
var xTo:Number = _stage.mouseX - _MouseSavedX + _which.x;
var yTo:Number = _stage.mouseY - _MouseSavedY + _which.y;
var limitingFuncReturnValue:int = _limitingFunc(xTo,yTo);
if(limitingFuncReturnValue == 0){//free to move.
_which.x = xTo;
_which.y = yTo;
}
else if(limitingFuncReturnValue == -1){//free to move Y
_which.y = yTo;
}
else if(limitingFuncReturnValue == -2){
_which.y = yTo;
}
//else:do nothing.
}
}
}
In "my actionscript theory", I'm supposed to see a circle that follows the mouse when I click it. (The draggable is not fully implemented) But the circle doesn't even budge :(
...I've been trying to figure out how to access the main class's stage property. I've googled for it, but still no progress.
Please help this helpless newb!!! I'll really appreciate your help:)
Thank you!

You're implementing your 2nd class as "draggable", when you named it (and it has to be named) "Draggable" with an upper case. Change it and see if that works. You seem to be passing in the parent classes stage correctly though.

If TestAPP is your document class. You can access to the stage thru the stage property (like your are doing in your example).
If TestAPP is not the document class, you should listen first to the ADDED_TO_STAGE event and then access to the stage property.
There is no need to add _mainStage property because you already have the stage property.
In order to move objects around, you need to use the ENTER_FRAME event.

You can access the main class' stage property the same way you do it for any DisplayObject on the stage: Use this.stage.
So just this should be enough:
var sp:Sprite = new Sprite();
stage.addChild(sp);
You could then pass the sprite and the main class' stage as a parameter, the way you did - but I would recommend creating a subclass Draggable extends Sprite and instantiate the whole thing using new Draggable() instead.
The new object will automatically have its own reference to stage as soon as it is added to the display list, and limitingFunc could be a member of Draggable, as well.
Also, you can use the startDrag() and stopDrag() methods, no need to implement your own: You can specify a Rectangle as a parameter to startDrag() to limit the permitted movement.

if you need it so much you may:
private static var _instance: TestAPP;
//...
public function TestAPP(){
_instance = this;
//...
}
public static function get instance():TestAPP{
return _instance;
}
public static function set instance(newVal: TestAPP):void{
_instance = newVal;
}
and you'll be able to reference its stage from any class where your TestAPP will be imported just with TestAPP.instance.stage.
but _instance is a property of the class TestAPP itself, not its instances

Related

Addchild - object not appearing on stage from external class

I'm having problem with "DiamondEnemy" objects created in external class "Level" not appearing on the stage. I'm trying to retrieve a random enemy from "EnemyNotReleasedArray" at intervals and add them to screen through "enemyOnScreen" sprite.
Please note I have not 100% finished with all the functionality; so it may seem a little weird. I don't want to go further until I can actually get it working.
update: I create a new "level" object from a separate document class called "main".
package {
import DiamondEnemy;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.display.Sprite;
import flash.display.MovieClip;
public class Level extends MovieClip {
private const START_DELAY_SECONDS:uint = 1000;
private var EnemyNotReleasedArray:Array = new Array();
private var startDelay:Timer;
private var enemyOnScreen: Sprite;
public function Level(NumberDiamonds:uint)
{
// load the required enemies into the array
loadEnemyArray(NumberDiamonds);
//setup up sprite, for enemies that will appear on the screen
enemyOnScreen = new Sprite();
addChildAt(enemyOnScreen, numChildren);
// create delay timer before enemies can start appearing on screen
startDelay = new Timer(START_DELAY_SECONDS,1);
// set eventlistener that once delay finishes
startDelay.addEventListener(TimerEvent.TIMER_COMPLETE, releaseRandomEnemy);
startDelay.start();
//setup up sprite, for enemies that will appear on the screen
enemyOnScreen = new Sprite();
addChild(enemyOnScreen);
}
// creates the requested number of enemies type into EnemyNotReleasedArray so they can be released later
private function loadEnemyArray(numDiamonds:uint)
{
// use requested number diamonds enemies - to create diamond enemy objects
for (var i:uint = 0; i < numDiamonds; i++)
{
var diamond:DiamondEnemy = new DiamondEnemy();
EnemyNotReleasedArray.push(diamond);
}
}
// selects a random enemy from EnemyNotReleasedArray and resizes the array so enemy is no longer in it
private function releaseRandomEnemy(evt:TimerEvent)
{
var arrayLength:uint = EnemyNotReleasedArray.length;
// check make sure array is not empty, if empy level is over
if (arrayLength > 0)
{
var randomArrayIndex = Math.ceil(Math.random() * arrayLength) -1;
/// adding random enemy to sprite object
enemyOnScreen.addChild(EnemyNotReleasedArray[randomArrayIndex]);
trace(EnemyNotReleasedArray[randomArrayIndex]);
//remove the enemy from array and make element null
EnemyNotReleasedArray.removeAt(randomArrayIndex)
//tempory array to store non-null values
var tempArray:Array = new Array();
// cycle through EnemyNotReleasedArray and store all values that are not null into temp array
for each(var enemy in EnemyNotReleasedArray)
{
if (enemy != null)
{
tempArray.push(enemy)
}
}
// save temp array value into EnemyNotReleasedArray
EnemyNotReleasedArray = tempArray;
}
else
{
trace("no more enemies left in array");
}
}
}
}
document class "Main":
package {
import Level;
import DiamondEnemy;
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var level:Level = new Level(1);
}
}
}
The display list is a hierarchical graph, often called a tree.
Everything that's directly or indirectly connected to the root node is displayed. The root node is the Stage object. While possible, nothing of your own code should actually addChild() to this object. (for reasons out of the scope of this answer)
The only child of the Stage is the instance of your document class that's created when your .swf file is executed. This instance is automatically added to the Stage object, too, which is why you never have to add the document class to anything but it's still visible.
The constructor of your Main class looks like this:
public function Main()
{
var level:Level = new Level(1);
}
The problem is that while you successfully create the Level object, it is never added to the above described hierarchy that's usually called the "display list". level is not connected to the root node, which is why it is not displayed. You can still add children to level, but they won't be visible either for the same reason: that is, level is not visible.
To fix this, add level to your document class like so:
public function Main()
{
var level:Level = new Level(1);
addChild(level);
}
Btw. you have this code twice:
//setup up sprite, for enemies that will appear on the screen
enemyOnScreen = new Sprite();
addChildAt(enemyOnScreen, numChildren);
and
//setup up sprite, for enemies that will appear on the screen
enemyOnScreen = new Sprite();
addChild(enemyOnScreen);
but you only need it once. The second one is all you need.
And neither one of your two classes should extends MovieClip as non of them have a time line. Use extends Sprite unless you are actually dealing with MovieClips.

Best way to read a property from a MovieClip?

I got a .fla file, where inside I have some movieclip instances placed in the scene. I need to iterate through them and gather some data, like position, name, and custom properties.
These custom properties, I don't know how to pass them, I know one way that works so far is to use the accessibility properties panel (Flash Pro CC), and then in the code I can just read them. However there should be a better way I assume.
If I have understood correctly your question and what you have said in your comments about the answer of #Aaron, you have an swf file, which you load dynamically, and you want to get/set some of its MovieClips properties, if it's the case, take this example :
MyMC.as :
public class MyMC extends MovieClip
{
private var timer:Timer;
private var rotation_speed:int = 1;
public function MyMC() {
}
public function set_Rotation_Speed(_rotation_speed:int): void {
this.rotation_speed = _rotation_speed;
}
public function get_Rotation_Speed(): int {
return this.rotation_speed;
}
public function start_Rotation(): void {
this.timer = new Timer(500, 10);
this.timer.addEventListener(TimerEvent.TIMER, on_Timer);
this.timer.start();
}
private function on_Timer(e:TimerEvent): void {
this.rotation += this.rotation_speed;
}
}
Then, in my swf.swf I have an instance of that MovieClip.
I loaded the swf.swf using this code :
var loader:Loader = new Loader()
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, on_SWFLoad);
loader.load(new URLRequest('swf.swf'));
And to set/get some of my MovieClip properties, I did :
function on_SWFLoad(e:Event): void
{
var swf:DisplayObjectContainer = DisplayObjectContainer(loader.content);
var num_children:int = swf.numChildren;
for(var i:int = 0; i < num_children; i++)
{
var child:MovieClip = MovieClip(swf.getChildAt(i));
// get the name
trace('name : ' + child.name);
// set the position
child.x = child.y = 100;
// get the class name, in my case it's MyMC
var class_name:String = getQualifiedClassName(child);
// get all the details of the child
trace(describeType(child));
child.set_Rotation_Speed(45);
child.start_Rotation();
trace(child.get_Rotation_Speed()); // gives : 45
}
addChild(loader);
}
You can use the describeType() function To get all the properties of your instance.
Hope that can help.
First of all, you can set properties on timeline instances from code. There's nothing special about this. For example:
Place an instance of a library symbol on a keyframe
Give it an instance name in the Properties panel, for example "myInstance"
On the same keyframe put some code that refers to it, such as myInstance.color = "red"
You can also create and assign custom properties by making the symbol a component:
Right-click on the symbol in the library and choose "Component Definition"
Add custom properties in the Parameters table. It's now a component symbol.
On the timeline, place an instance of the symbol and use the Properties panel to set its parameters.
You can do a lot more with components if you want, such as live preview and compiled components. More info can be found here here: http://www.adobe.com/devnet/flash/learning_guide/components/part03.html

Action Script 3.0 Mouse Event in a class package

am having problem with using mouse click event inside a class, i am an absolute beginner to Action Script.
what i want is that if i click the btn_MClick button it should run the script, but everytime i click it i get error message that btn_MClick is undefined.
btn_MClick is on stage and with the instance name if btn_MClick
public class gunShip1 extends MovieClip
{
var moveCount = 0;
public function gunShip1()
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveGunShip1);
stage.addEventListener(KeyboardEvent.KEY_DOWN, ShootGunShip1)
btn_MClick.addEventListener(MouseEvent.MOUSE_DOWN.KEY_DOWN, ShootGunShip1);;
}
function ShootGunShip1(evt: MouseEvent)
{
var s_Bullet:survBullet = new survBullet();
var stagePos:Point = this.localToGlobal (new Point(this.width / 2-10, this.height));;
s_Bullet.x = stagePos.x;
s_Bullet.y = stagePos.y;
parent.addChild(s_Bullet);
//play sound
var gun_sound:ricochetshot = new ricochetshot();
gun_sound.play();
}
}
Please, i have absolutely no idea what to do, and somehow it feels like the whole process is wrong.
Your class gunShip1 does not have the property btn_MClick, the root, or document class does.
Basically what's happening is that you've placed your button on the stage, which makes it an instance that belongs to the root container. At the moment, you're trying to refer to the button as a property of gunShip1.
What you should really do here is have the button click managed separately to gunShip1, and have that separate code invoke methods of gunShip1. For example, you could have this in your document class:
public class Game extends MovieClip
{
private var _ship:gunShip1;
public function Game()
{
_ship = new gunShip1();
// The Document Class will have reference to objects on the stage.
btn_MClick.addEventListener(MouseEvent.CLICK, _click);
}
private function _click(e:MouseEvent):void
{
_ship.shoot();
}
}
And then your updated shoot method in gunShip1:
public function shoot():void
{
var s_Bullet:survBullet = new survBullet();
var stagePos:Point = this.localToGlobal (new Point(this.width / 2 - 10, this.height));
s_Bullet.x = stagePos.x;
s_Bullet.y = stagePos.y;
parent.addChild(s_Bullet);
var gun_sound:ricochetshot = new ricochetshot();
gun_sound.play();
}
The idea is that the gunShip1 should not be responsible for dealing with user input (mouse, keyboard, etc). Instead, that should be a separate class which informs gunShip1 that it should do something.

my dynamically added movieclips have a name of "instance XX"

There are a couple things going on here that I dont fully understand. I have created a custom class that extends MovieClip to give some custom properties and create a geometric shape inside of the created MovieClip
package com.hyatt
{
import flash.display.*;
import flash.geom.*;
public class mapPin extends MovieClip
{
public var spirit:String;
public var callName:String;
public var hotelName:String;
public var city:String;
public var s:String;
public var zip:String;
public var country:String;
public var brand:String;
public var featured:Boolean;
public var horizon:Boolean;
private var _mc1:MovieClip = new MovieClip();
public function mapPin(_brand:String)
{
brand = _brand;
switch (_brand)
{
case "Andaz":
pinCircle(0xff0000);
break;
case "Grand Hyatt":
pinCircle(0x0000ff);
break;
case "Hyatt":
pinCircle(0x4600f0);
break;
}
}
private function pinCircle(color:uint):void
{
_mc1.graphics.beginFill(color);
_mc1.graphics.drawCircle(0,0,20);
this.addChild(_mc1);
_mc1.graphics.endFill();
}
}
}
Then I'm adding an couple instances of the mapPin class to a container movieclip on my stage and adding an event listener to that container clip.
var myTest1:mapPin = new mapPin("Andaz");
myTest1.brand = "Andaz";
container_mc.addChild(myTest1);
myTest1.name = "myTest1" //this is added purely for testing the "instance xx", same result
myTest.x = 100;
myTest.y = 100;
var myTest2:mapPin = new mapPin("Hyatt");
container_mc.addChild(myTest2);
myTest2.brand = "Hyatt";
myTest2.x = 400;
myTest2.y = 400;
container_mc.addEventListener(MouseEvent.CLICK, pinClicked);
finally I'm trying to be able to access the properties (the only one set thusfar is "brand") of the mapPin that is clicked.
function pinClicked(e:MouseEvent):void
{
trace(e.target.name); // traces "instance xx" instead of "myTest1"
trace(e.target.brand); // traces "undefined"
}
I can add the mapPin instances, and adjust their x and y though i cannot reference the custom class properties like "brand" and their name becomes a generic instance name. What am I missing? There are going to be upwards of 500 of these items added and I want to be able to pull information from them based upon a users click.
I'd have to see your mapPin class to be sure, but I think that the DisplayObject that is dispatching the event, is a child of mapPin.
To fix this, inside your mapPin class constructor add this line :
mouseChildren = false;
That will specify that children shouldn't receive clicks/dispatch mouse events.
currentTarget is the most recent object to dispatch an event and target is the object that originally dispatched it
It's not. AS3 doc says :
currentTarget :
The object that is actively processing the Event object with an event listener. For example, if a user clicks an OK button, the current target could be the node containing that button or one of its ancestors that has registered an event listener for that event.
target : The event target. This property contains the target node. For example, if a user clicks an OK button, the target node is the display list node containing that button.

Noob AS3 question regarding using event handlers to remove MovieClip object from stage

I'm an AS3 noob just trying to get more comfortable working with event handlers in Flash and build interactivity into my application.
In the code below, I have created an instance of the DrawLineChart class called LineChart1. When I test the movie, it shows up on the stage just fine and when I click on it, I can use a trace command to get a string statement written to the output window.
However, now I want to be able to click on LineChart1 on the stage and have it be removed. When I do that, I get an error message 1120: Access of undefined property LineChart1.
Could someone please explain to me why I'm unable to refer to my instance LineChart1 and what I need to do so that I can refer to it and remove it when it gets clicked? Also, I'd love to know why the trace statement works when I click on LineChart1 during runtime, but not the removechild command.
I'm sorry if this question is too simple, but thank you all for your help in advance. I really appreciate it.
package{
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip{
var recWidth:Number = 250;
var recHeight:Number = 550;
var recX:Number = 50;
var recY:Number = 50;
var recScaleY:Number = 30;
public function Main(){
var LineChart1 = new DrawLineChart(recX, recY, recWidth, recHeight, recScaleY);
LineChart1.addEventListener(MouseEvent.CLICK, onClickHandler);
addChild(LineChart1);
}
function onClickHandler(e:Event):void{
trace("hello"); // This works. When I click on the LineChart1 MovieClip on the stage during runtime, I get "hello" as an output.
removeChild(LineChart1); // throws an error 1120: Access of undefined property LineChart1. Why?
}
}
}
Your variable is scoped locally to Main, you need to declare it as an instance variable (class level), to properly define its scope.
private var _lineChart1:DrawLineChart;
//main function
_lineChart1 = new DrawLineChart(...
//handler function
this.removeChild(_lineChart1);
For more information about scope in AS3 = check out the livedocs.
Cheers
Your problem is that you have defined LineChart1 as a local variable. This means that because you declare it inside a function, it is only visible within that function.
Make LineChart1 a property of your class, then you will be able to see it from your event handler. Alternatively, use e.target as DrawLineChart.
All answer's is good but if u have more then one on the stage what can u do ?
You can use an Array to take a list of your mc's and then u can use that Array to remove mc's on the stage.
Here is a Simple Example:
package
{
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip{
private var recWidth:Number = 250;
private var recHeight:Number = 550;
private var recX:Number = 50;
private var recY:Number = 50;
private var recScaleY:Number = 30;
private var lineArray:Array = new Array();
public function Main()
{
for(var i:int = 0;i<10;i++)
{
var LineChart1 = new DrawLineChart(recX, recY, recWidth, recHeight, recScaleY);
LineChart1.addEventListener(MouseEvent.CLICK, onClickHandler);
LineChart1.name = line+i.toString(); // u can use whatever u want for name's
lineArray.push(lineChart1);
addChild(LineChart1);
}
//if u want to place this 10 LineChart1 u can set x and y values like recX += recX and ect.
}
private function onClickHandler(e:Event):void
{
//when u click one of your LineChart1 and want to remove it from stage u can use this
trace(e.currentTarget.name); // if u want to see what is the name of ur mc
var myId:String = e.currentTarget.name.substring(4,10);
removeChild(getChildByName("line"+myId));
}
}
hope it works for u