Referring to a variable in the target file from a class AS3 - actionscript-3

I've researched this all day and I'm stuck! I want to know how to use a variable from a target path to move something. Here is the code:
// Target file
var speed:int = Number(1);
var container:MovieClip = new MovieClip;
var objects:Objects = new Objects();
container.addChild(objects);
and
// Class for Objects
package {
import flash.display.MovieClip;
public class Objects extends MovieClip {
public function Objects() {
this.x += speed;
trace(this.x);
}
}
}
When I run it, I get an error like this:
Objects.as, Line 6 1120: Access of undefined property speed.
Thanks!

The most simple way to do it is to pass the speed variable to that class. See, classes are like stand alone objects in the space, and they (mostly) don't know about each other, or at least they don't know about the variables that you define in them.
So if you create a class inside another one (new Objects()), the most easy way is this:
var objects:Objects = new Objects(speed);
Then, inside Objects class, you will have:
public class Objects extends MovieClip {
var _speed:Number;
public function Objects(speed:Number) {
_speed = speed; // save it to a local member variable
this.x += _speed;
// start working with the local one,
// which will be accessible in the whole class
}
It's like passing some defined values, so that class can use them. And the class saves them inside itself so it can be used through the whole file (scope).

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

How is this class connecting to the sound?

im trying to use (well succeeding) to use this sound class
http://www.mcfunkypants.com/2011/as3-pitch-shift-mp3/
the example code looks like this. . .
public class Pitch_Shift_Example extends Sprite
{
[Embed(source='Pitch_Shift_Example.mp3')]
private var engine_mp3 : Class;
public var engine_loop:Pitch_Shift_MP3;
public function Pitch_Shift_Example()
{
engine_loop = new Pitch_Shift_MP3(engine_mp3);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
var someText:TextField = new TextField();
someText.x = 200;
someText.y = 0;
someText.textColor = 0xFFFFFF;
someText.selectable = false;
someText.autoSize = TextFieldAutoSize.LEFT;
someText.text = "Pitch Shift MP3 Demo by Breakdance McFunkypants\nMove your mouse to change the sample rate. Enjoy!";
addChild(someText);
}
private function onMouseMove(event:MouseEvent):void
{
engine_loop.rate = (mouseX / stage.width) * 2;
}
}
Now what is confusing me is how the engine_mp3 class uses the sound.
line one embeds the sound.
line two creates an empty class called engine_mp3.
line 3 creates a new pitch shift class which in line 7 we pass the (empty) engine_mp3 class.
Basically how is engine_mp3 getting the mp3 data??
Any help is appreciated.
Andy
It works like something like this:
You embed the mp3 file with these two lines:
Embed(source='Pitch_Shift_Example.mp3')]
private var engine_mp3 : Class;
Those two lines work together. The first line is embed metadata that describes the line below it. What it's doing is embedding the mp3 file and making it accessible as a class. You therefore have a reference to this embedded sound class with the variable name engine_mp3.
Later on in your code, you instantiate a new instance of the Pitch_Shift_MP3 class, and you pass in the reference to your embedded sound class engine_mp3:
engine_loop = new Pitch_Shift_MP3(engine_mp3);
The constructor for the pitch shift class is expecting a class (that represents a sound) as its single argument. What it must be doing in its own code is instantiating the class that engine_mp3 represents by doing this:
var instantiatedSound:Sound = new engine_mp3() as Sound;
Hope that makes sense!

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

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

Actionscript 3: Array Scope in a Document Class

I have the following function to set up cards in a game. I created one array to hold the kind of cards, and another array to hold the position of the cards.
private function setPlayerCard(cardNumber:int, cardPos:int):void{
for (var i:int = 1; i < _CardGridInstance.numChildren+1; i++) {
var _position:MovieClip = MovieClip(_CardGridInstance.getChildByName("Position_" + i));
cardPositions[i] = _position;
cardPositions[i].pos_name.text = "position" + i;
cardPositions[i].id = ["pos"+i];
}
for (var j:int = 1; j < numCards+1; j++) {
var _c:Class = getDefinitionByName("Card_" + j) as Class;
var _cardInstance:MovieClip = new _c();
cards[j] = _cardInstance;
}
cards[cardNumber].x = _CardGridInstance.x + cardPositions[cardPos].x - 1;
cards[cardNumber].y = _CardGridInstance.y + cardPositions[cardPos].y;
addChild(cards[cardNumber]);
}
So if I want to set the card number "3" in position "5" I just write:
setPlayerCard(3,5);
The problem I can see is that every time I'd like to place a card, I am creating two arrays every time. I would like to make the arrays "global" (i.e. create it in my constructor in my document class) and reuse it in the function "setPlayerCard" however I am getting errors when I try to do so.
Any suggestions?
This is a perfect case for a Singleton static class data model. You can get the instance of the Singleton from throughout the application as it is a static class, and it can contain the two arrays without duplication.
pixelbreaker has a nice basic Singleton AS3 example that you can build from.
It's a little difficult to answer accurately without knowing how you are creating the variables and what errors you're getting. Can you post the entire class and the errors?
I can, however, recommend that you do not use the Singleton pattern. This is not a perfect case for a Singleton. The Singleton pattern has no place in OOP, it's procedural programming wrapped up like OO, but that's an argument for elsewhere.
This is, though, a perfect case for a class level variables. The following is a simple example. There are a few missing variable declarations though (numCards), as I don't know where you're creating and setting them.
package{
import flash.display.Sprite;
public class CardGame extends Sprite{
private var cardPositions:Array = new Array();
private var cards:Array = new Array();
public function CardGame(){
for var i:uint = 1; i <= _CardGridInstance.numChildren; i++) {
var position:MovieClip = MovieClip(_CardGridInstance.getChildByName("Position_" + i));
cardPositions[i] = position;
cardPositions[i].pos_name.text = "position" + i;
cardPositions[i].id = ["pos"+i];
}
for(i = 1; i <= numCards; i++) {
var c:Class = getDefinitionByName("Card_" + i) as Class;
var cardInstance:MovieClip = new c();
cards[i] = cardInstance;
}
}
private function setPlayerCard(cardNumber:uint, cardPos:uint):void{
cards[cardNumber].x = _CardGridInstance.x + cardPositions[cardPos].x - 1;
cards[cardNumber].y = _CardGridInstance.y + cardPositions[cardPos].y;
addChild(cards[cardNumber]);
}
}
}
This way you only create and populate the arrays once and you can access them from anywhere within the CardGame Class. They are not global but they are within the scope of the setPlayerCard method.
You may get errors as objects might not be instantiated when the Document Class' constructor gets called, but that can be worked around.
What is the need for the variable to be public and static?
Static means that the variable is on the Class, not instances of the Class. So every "CardGame" instance will share the same static variable. I presume, because this is the Document Class, that you will not have more than one instance of it. So there is no reason for that.
The only other reason, because you declared it public, is to make the variable accessible from outside the Class through CardGame.cardPositions. This is bad practice as you shouldn't allow other objects to directly manipulate a Classes internal data. That breaks encapsulation. Since this is the Document Class and the top of the hierarchy, you should pass a copy of the data to whichever object needs it and wait for an event to retrieve the updated data. That way you can sanitise the data before using it and you're not just blindly trusting other objects to respect your data.
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)
I tried using the Singleton class, but since I had to reference MovieClips that were already on the display list, I opted for a different solution from "Actionscript 3 Tip of the Day":
http://www.kirupa.com/forum/showthread.php?p=2110830#post2110830
package {
public class ClassName {
public static var myArray_1:Object = new Object;
public static var myArray_2:Object = new Object;
public function ClassName() {
//constructor
Whatever();
DoStuffWithWhatever();
}
private function Whatever() {
// put stuff into the array here
}
private function DoStuffWithWhatever():void {
// do stuff with the array values here.
}
}
}