I recently picked up Emanuele Feronato's Flash Game Development by Example to try and help expand my knowledge of game design and actionscript 3, and I'm stuck on the first chapter where we're building basically a memory match two game where you have ten sets of tiles and you try and match them up by clicking on them.
In the code I'm stuck on, Mr. Feronato is adding the tiles to the stage using a for loop and addChild, here's the code in particular:
// tile placing loop
for (i:uint=0; i<NUMBER_OF_TILES; i++) {
tile = new tile_movieclip();
addChild(tile);
tile.cardType=tiles[i];
tile.x=5+(tile.width+5)*(i%TILES_PER_ROW);
tile.y=5+(tile.height+5)*(Math.floor(i/TILES_PER_ROW));
tile.gotoAndStop(NUMBER_OF_TILES/2+1);
tile.buttonMode = true;
tile.addEventListener(MouseEvent.CLICK,onTileClicked);
}
// end of tile placing loop
In the 5th line, you can see that he creates a custom property of the tile variable called "cardType," but when I try and run the code I get the error "Access of possibly undefined property cardType through a reference with static type Tile." I have the class Tile extending MovieClip, and the main class extends Sprite, but as far as I can tell I've written the code exactly as in the book and can't get past this. I thought about just using a normal int variable cardType to hold tiles[i] but later on you use the cardType property on a mouse event so I'm a little stuck.
Has something changed in Flash that no longer allows you to create custom properties in this way? Or did I just do something stupid that I'm not catching.
As always, thank you so much for the help.
ActionScript supports dynamic classes, in which properties may be added during runtime. Without the dynamic keyword, a class is sealed, meaning its properties may not be altered.
MovieClip is an example of a dynamic class.
Instantiating a MovieClip from code (or MovieClip instance created in Flash Professional), adding this property would be supported:
var tile:MovieClip = new MovieClip();
tile.cardType = "custom value";
However from code, even if extending MovieClip you must add the dynamic keyword:
package {
import flash.display.MovieClip;
public dynamic class Tile extends MovieClip {
/* ... */
}
}
Now, you may add properties to your Tile instance:
var tile:Tile = new Tile();
tile.cardType = "custom value";
(Note that there is nothing really 'wrong' here with my code (in the sense that it works), but more wondering on how it is working and what is happening under the hood)
Currently I have two libraries, each with one object. One is set with the class "Apple" and the other is "Pear", They are located in separate external swfs.
Apple's base class is MovieClip as content-wise it is a movieclip: has frames + animation
And here is the code I used to create and display an Apple movieclip object:
function getClip(inputName, spriteLibrary:Loader):MovieClip {
var aClass:Class = spriteLibrary.contentLoaderInfo.applicationDomain.getDefinition(inputName) as Class;
return (MovieClip) (new aClass());
}
this.addChild(getClip("Apple", referenceToTheLoadedSwfThatHasAppleInIt));
The above works just fine and Apple appears on the stage and plays.
However, Pear's base class is a Sprite (has no animation, frames, etc). So the above fails, since the method is supposed to return a MovieClip.
this.addChild(getClip("Pear", referenceToTheLoadedSwfThatHasPearInIt));
I thought for a moment I would have to have two versions of the above method, one for Sprite and one for MovieClip. But just to see, I changed it to Sprite and tried to create Apple:
function getClip(inputName, spriteLibrary:Loader):Sprite {
var aClass:Class = spriteLibrary.contentLoaderInfo.applicationDomain.getDefinition(inputName) as Class;
return (Sprite) (new aClass());
}
this.addChild(getClip("Apple"), referenceToTheLoadedSwfThatHasAppleInIt);
this.addChild(getClip("Pear"), referenceToTheLoadedSwfThatHasPearInIt);
Now both will work, but interestingly, I found that even though the method returns a Sprite, Apple still seems to work fine and plays it's animation on the stage. I can cast this to a MovieClip and access all the MovieClip related properties and so forth.
My question is then, when Apple "existed" as a Sprite, what happened to all of it MovieClip related "stuff" and is this actually a normal thing to do when having to work with MovieClips and Sprites (by pretending you only have Sprites and cast to MovieClip only when you need it?)
A Sprite class provides more basic functionality than a MovieClip, but everything that's a MovieClip can be manipulated by using the functionality of a Sprite class. In fact, your method can return as low a class as DisplayObject if your only intention is to do an addChild(). The typecast will not strip the typecasted object of any functionality, it will instead restrict the available calls for its properties. Say, a DisplayObject has x and y properties, a Sprite can be used to add objects to itself (the addChild() method) which the DisplayObject does not have, and a MovieClip has internal animation and a say gotoAndStop() method which a Sprite does not have. So, if you typecast an Apple to a Sprite, you cannot make a call to the reference's gotoAndStop(), because you've told the program that the reference is just Sprite. If you typecast the Apple or the Pear object to DisplayObject, you cannot call its addChild() method to say add a health bar (weird thing for apples to have health bars, but why not?), because the reference does not know that the underlying object supports this functionality. But actually nothing will happen to either object, no matter how you typecast them, you will just restrict yourself from applying more advanced actions via received reference.
Actually it's good practice to limit the functionality for yourself via typecasts, because you are then protected from making "crutches" over working code which can probably spoil its purpose. Say, your code will not be surprised in case you would decide to turn Apple into an advanced Sprite class (say, public class Apple extends Sprite {...}) with tailored properties, embedded event listeners, color-changing maybe, but if you typecast the newly created Apple to Sprite or DisplayObject, you are abstracting from any extra abilities of the instance and only use its basic interface (properties and methods of a typecasted class) to perform the actions the Sprite or DisplayObject are intended for. If you have some overridden properties in the advanced Apple, they will work as you have intended while making the Apple class, even if addressed from the superclass description - this is actually what override is for.
In short, don't worry about losing functionality, but try typecasting to least possible class with used functionality.
About "what happens under the hood": Each class has its own table of properties and methods, if a class is extending another class, that class's table is identical to the superclass's up to its end, and the extra space is occupied with information on properties and methods implemented by the class. If there is an override, the overridden method's info replaces the info of the corresponding method in the table for the class, not for the superclass though. Each instance has a memory block allocated to it, there is a reference to the class's properties and methods table, which is then used to execute correct code, if a method is called via instance reference. So, when you call a method, or a property with a getter or setter assigned to it, out of an instance, the following happens:
The correct set of parameters, including "this" pointer to that instance, is pushed into the stack. The correctness of the order and the type of parameters is ensured by Flash compiler.
Then, the class that's actually the proper class of that instance is referenced via prototype property of the instance. Every class in AS3 has this property. The instance is accessed via "this" pointer previously pushed into the stack. (Technically it's stored in more than just stack, so the reference is just the same that's put in the stack)
Then, the correct offset (determined by the compiler) is returned from that table, that is the address of the called method of the class. Overridden or not, no matter here, because an improper override is detected at the compile time, and will not happen here.
Then, code execution is transferred to the returned address. The method's code then parses the parameters in stack, does some more data protection and proceeds with its implementation.
The most important thing to understand here is that nothing happens to the object itself, but the way the compiler treats the object will differ.
For example, the compiler will treat the object returned by this function as a Sprite, even if we actually return a MovieClip:
function makeSprite():Sprite
{
return new MovieClip();
}
var test:MovieClip = makeSprite();
// 1118: Implicit coercion of a value with static type flash.display:Sprite
// to a possibly unrelated type flash.display:MovieClip.
So what we need to do here (as you understand currently) is tell the compiler that the result is actually a MovieClip via typecasting:
var test:MovieClip = makeSprite() as MovieClip;
Another thing to take notice of is that if you were to trace() the result, you would get [object MovieClip] rather than [object Sprite]:
trace( makeSprite() ); // [object MovieClip]
And using is to check if it's a MovieClip will return true:
trace( makeSprite() is MovieClip ); // true
Even if you used a more primitive type for test, your object would truly be a MovieClip:
var test:Object = makeSprite();
trace(test); // [object MovieClip]
Casting a MovieClip to a Sprite doesn't strip it of its MovieClip implementation; it simply tells the calling code (in this case, this.addChild()) that "this object is a Sprite; please treat it as such." The calling code doesn't know — or care — that it's really a MovieClip, because as long as it's a DisplayObject (which a Sprite, and in turn a MovieClip, derives from), the addChild() method will happily accept it.
Since a MovieClip is a Sprite anyway, it doesn't make any difference to the calling code. As for the Apple object itself, though, it's still a MovieClip at heart, and so will continue to function on its own like one.
As an analogy, think of going to the grocery store, buying some goods, and paying at the counter. You're a person with many different roles in various aspects of life, but as far as the cashier is concerned, you're just a customer making a purchase. That doesn't make you any less of a person, but the cashier doesn't have to be interested in who you are or what you do, beyond just a customer making a purchase.
TL;DR:
Your MovieClip will stay a MovieClip, but the reference that is down casted to a Sprite will be able to access only methods and variables that are available to Sprite.
ok im a noob for AS3 since i just started, i have two (2) movieclips inside a movieclip, the main mc is called main_mc then the two movieclips inside named trigger_mc and move_mc, trigger_mc has the instance name of start_ani, then inside the timeline of main_mc i have this code:
import flash.events.MouseEvent;
start_ani.addEventListener(MouseEvent.CLICK, correctans);
function correctans(e:MouseEvent):void {
move_mc.animate();
}
then i created a motion as actionscript 3.0 using move_mc then i inserted the code inside the timeline of the move_mc itself, and i made a function for that motion called animate, my problem is how do i access a function between two movieclips which both are inside another movieclip, i know this method is not programming wise, but i kinda need to learn this, pls help me, i badly need this, thank you in advance.
Parent is a property, not a function - you don't need ():
this.parent.move_mc.animate();
Also, you didn't mention the instance name of the move_mc movieclip, but access like the above requires the instance name to be move_mc - the movieclip symbol name doesn't matter in actionscript.
Update 1: To clarify, you said: trigger_mc has the instance name of start_ani
Good, then this code will work:
start_ani.addEventListener(MouseEvent.CLICK, correctans);
But you didn't say: move_mc has the instance name of ???
So we don't know if this code will work:
function correctans(e:MouseEvent):void {
???.animate();
}
Fill in those ??? for one.
Update 2: do you know if the click handler is being fired? Why not add a trace statement?
function correctans(e:MouseEvent):void {
trace("got click event!");
???.animate();
}
Because for a CLICK event, you need:
start_ani.buttonMode = true;
Though as you say, this isn't good programming practice because this assumes those two movieclips are siblings of the same parent. It's not extensible. If they're not, your code could throw errors. Just keep that in mind.
Background:
In a shared Object in AS3, I saved the names (as Strings) of certain MovieClips that appear on stage time in my game.
Issue:
I'm trying to reverse this and the only way I found so far is a method like this:
function objectNameFromString(objectNameAsString): MovieClip {
switch (objectNameAsString): {
case "myobject":
return myobject;
break;
}
}
The problem is that I have to make like 20 case statements to get this done which seems unneccesary =( is there anyway you can just reference a MovieClip from a String?
I think what you're looking for is this:
getChildByName()
It's not a great way to code, because it bypasses type safety, but properties of any object, including your timeline, are stored as an array anyway, so if both your property and your function are in the same scope (i.e. both variables are on the main timeline, regardless of where your MovieClip is in the display heirarchy), you should just be able to use this["myObject"];:
import flash.display.MovieClip;
var myObject:MovieClip = addChild(new MovieClip()) as MovieClip;
trace(this["myObject"]);
// Output: [object MovieClip]
But building a formal collection, as others have suggested, is probably a better approach, or using getChildByName() if all your objects share a common parent (e.g. if they are all placed directly onto the stage).
I have to call a function that is defined on the main stage of my project, and I have to call it from a MovieClip, what can I do?
I'm using flash cs5.5 and AS3.0
There are multiple ways to access the MainTimeline from objects on the stage. Probably the most reliable is 'root', but there is also 'parent' (but only if you MovieClip is a direct child of the main timeline).
// root should always work
Object(root).myFunc();
// parent will only work if your movieclip is a direct child of the main timeline
Object(parent).myFunc();
Note that you have to cast these are generic Objects (or MovieClip would work) because they return typed classes that don't have a 'myFunc' function in them.
You'll need this code on your main timeline:
function myFunc():void {
trace("My function got called!");
}
When I read your question it sounds as though you have a function defined in an action frame of your main timeline.
My answer may be out of reach for your current project, and ToddBFisher's answer is perfectly right. That said - I'm going to answer the question differently.
Instead of defining a function on the main timeline, set up a document class, define your functions there, and access the class's functions in your code. Keep as much code off your timelines as possible.
Downloadable files for Document Class example: http://www.isgoodstuff.com/2008/06/06/actionscript-30-documentclass-in-plain-english/
Setting up an AS3 class: http://www.adobe.com/devnet/flash/quickstart/creating_class_as3.html
Assuming your movie clip is a direct child of your main stage, in you movie clip you can do:
MovieClip(parent).theFunctionToCall();
if your MovieClip has a class, just add it to your main class using like:
var m:MovieClip = new MovieClip();
**addChild(m);
then you can get access into it's public function like typing:
m."functon name";