What happens to a Movieclip when casted as a Sprite? - actionscript-3

(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.

Related

AS3 - Get reference of instance who called a function

I have two classes, "player" and "Stickman", "player" extends "Stickman".
The class "Stickman" contains functions that all characters will have in the game such as moving and handling animations (all use the same animations) while the "player" class contains player-exclusive actions.
When I call an action from a player instance that is inherited from the "Stickman" base class, how do I correctly point (from the stickman base class) to that player instance that called the function?
For example, to handle running animations
Trigger code in player.as:
moveType("running");
moveType function in Stickman.as:
public function moveType(type:String):void{
this.gotoAndPlay("running");
trace("testing");
}
To start playing frame with frame label "running" inside the player object (which is an extend of Stickman). I know this doesn't work, so I would like to replace this with something that would point to the caller of the function (i.e. the player object).The trace statement still runs so there (should) be no problem with the linkage. Is there a way to do this without passing the childId of the player object?
It's all about so called inheritance. Player IS actually a Stickman. So when you call moveType you are calling it ON Player object. If you don't have such function inside it, it looks up in the inheritance chain, and finds it inside Stickman.
Inheritance is one-directional. Which means that Player knows all methods of Stickman, but the opposite is not true - Stickman works only by itself and does not know who extended it and who not.
That said - you must use the Player class as a starting point and decide what to do from there on.
Long story short - you need to override the function from Stickman into Player and then decide what to do and what not. Example:
Stickman:
public function move(type:String):void {
this.gotoAndPlay(type); // regular workflow for reach Stickman
}
Player:
override public function move(type:String):void {
if (type == 'player_specific') {
this.gotoAndPlay('frameAvailableOnlyForPlayerClip');
// maybe something else, player specific, like play sound or smth else
} else {
super.move(type); // calls whatever is inside Stickman::move
}
}
Remember - this is always the very same object that you are calling the function for. But you can decide if you want something specific (player case) or you want the very usual flow. super.move will call what's in the parent class.
Inheritance is very important and I cheer you for deciding to use it! With a little more practice you will master it!

AS3 Accessing stage objects from a class

I'm writing a class Player, and I'm developing collisions but flash gives me an error to this line:
function checkCollision(event:Event)
{
if (this.hitTestObject(stage.wall)) // THIS LINE!!!!!!!!
{
trace("HIT");
}
else
{
trace("MISS");
}
}
}
The error is:
Access of possibly undefined property wall through a reference with
static type flash.display:Stage.
How can I access to wall ? wall is a symbol on the stage... Should I develop it in another way? please help
MovieClip is a dynamic object, whereas Sprite or Stage are not. With these classes, unless the property is explicitly defined, the compiler will complain about the absence of the property when you try to reference it.
If you're programming with Flash IDE, "Automatically Declare stage Instances" will create properties on your stage that make dot.notation pathing possible.
If you're creating objects dynamically, you'll have to either create the properties yourself (which is impossible with static classes like Sprite), or reference them by DisplayList fetching methods getChildAt() or getChildByName().
In the case of a class, unless you extend a class that is already a DisplayObject, you won't inherently have access to stage or root. You'd have to pass a reference to the MainTimeline or Stage manually (probably during instantiation). Even if you did extend a DisplayObject, you'd have to first parent the object to the stage; until then, the properties are null.
For the sake of argument, let's assume Player class is extending Sprite, and you have parented it to the stage. Your code would correctly be written as follows:
function checkCollision(e:Event) {
if (this.hitTestObject(this.root.getChildByName("wall"))) {
trace("HIT");
} else {
trace("MISS");
}
}
Notice that the call to "wall" is not on the Stage. That's because there is only one child of stage, and that's MainTimeline (a.k.a. root).
BTW, you had an extra close brace in you example.
Yep if you have automatically declare stage instances unchecked you will get that error. It is a good practice to declare everything in AS3 and not rely on the GUI to do it. Even if it is
Public var boringBackground:Sprite;
It will pay off in the end performance and coding wise.

as3 Variables changing impossibly

I'm having an issue with a "Point" type variable velocity changing without any calls to change it.
private function framecode(e:Event) {
trace(getVelocity().y);
tracks.gotoAndStop(2);
trace(getVelocity().y);
}
This code is part of a class called "tank" which extends the one that velocity is used in (my moving object class). velocity is a private point type variable and getVelocity() is a public access method. tracks is a named movieClip contained inside the one linked with tank. The event listener is ENTER_FRAME. There is no coding on the frames of tracks.
Somehow these two traces give different values (the first one being correct) and I can't figure out how the gotoAndStop() can possibly effect it (and hence how to fix it).
I've found that play() does not reproduce the bug but prevFrame() and nextFrame() do. As the variable is private this class should not even have access to it to change it.
Another strangeness is that if the event listener is changed to FRAME_CONSTRUCTED or EXIT_FRAME, there is massive lag and my movieClip randomly disappears after a few seconds.
Thank you for reading, any help would be appreciated.
Your velocity variable is private, so one one can access that outside of the class.
However, getVelocity() is returning a reference to your velocity variable. Once someone has that reference, they can change the values of it's properties: getVelocity().y = 3. So it's not impossible for this to happen.
One way to trouble shoot this is to add a trace() statement to/set a breakpoint in getVelocity() so you can see where it's being used.
You could do something similar w/the Point class, but you'll have to extend it, add getter/setter methods for y (that traces out when they are called), and modify your code to use the getter/setter. This might be worthwhile (its simple enough), and the act of modifying your code to use the getter might help you discover where the problem is.

Using a MovieClip as a parameter in AS3

I'm having problems attaching MovieClips to different instances of a class. I'm kinda new to ActionScript 3, honestly, so this question might be a bit noobish. I did the research, though, but haven't found the kind of answer that I expected.
function AddNewElement(clip:MovieClip, array:Array, name:String, firstValue:int, secondValue:int):Element
As you may be able to guess, this is the function I made to create instances of a class in a dynamic way and add them to an array. I'm having problems with the very first parameter, though. How do I pass a MovieClip from my library to this function?
I saw a lot of answers to problems similar to this one stating that each MovieClip should be a class on its own, but since I have like forty MCs and I want to use them all for more or less the same thing I feel that it kills the purpose of classes, really.
How should I approach this?
First you need to give your library symbols unique class names from the linkage section while exporting or later from the "properties" option. You will see this input when you check the "Export for ActionScript" option there. Then you will need to instantiate your library symbol (with the new keyword) and cast it to MovieClip to pass to this function. So
AddNewElement(new LibrarySymbolClass() as MovieClip,[],'etc',0,0);
AddNewElement(MovieClip(new LibrarySymbolClass()),[],'etc',0,0);
will both let you do what you want.
However, since not all your library elements need to extend the MovieClip class, you would better pick DisplayObject instead of MovieClip. So a better version of your function would be
import flash.display.DisplayObject;
function AddNewElement(clip:DisplayObject, ...):* {
// some code here
return clip;
}
var clip:LibrarySymbolClass = AddNewElement(new LibrarySymbolClass() as DisplayObject,[],'etc',0,0);
trace(clip);
Using asterisk in the return value type will let it return the object with its right type (as [object LibrarySymbolClass] in this example).
Why not to create the movie clips on runtime, aka. creating them in the runtime execution context then instantiating each of them at the moment when you invoke the class. If each of the MC's is different, then either you create a MC class for each of them, giving them a name which end up in a ascending number, then using a for loop you put them in an array like so:
var mc_num:int = 40 // the number of MovieClips
var arr:Array = new Array();
for (var i:int=0; i < mc_num; i++) {
arr.push("myMovieClip" + String(i));
}
..then making reference to each of them by using the array index. I skip the part where you associate the images to MovieClips.
After that you invoke the desired MC as below:
var mc_1:MovieClip = arr[1] as MovieClip;
stage.addChild(mc_1);
When you create a MovieClip in Flash it gives you certain options, one of those options is for Flash to create a class for that MovieClip. That being said if you have that option applied to all forty movie clips then you create something like a master movie clip class and have each movie clip class extend the master movie clip class. The only thing is that you would have to create a .as file for each of your 40 movie clips and add extends MasterMovieClip to the class declaration. For example:
public class MasterMovieClip extends MovieClip {
// All of the variables and methods pertaining to each movie clip go here
}
Then each individual movie clip would resemble this class.
public class IndividualMovieClip_1 extends MasterMovieClip {
// Just include a constructor, even though you don't have to
}
Now all of your individual movie clips will have the same methods and variables, as long as said methods and variables are public, not private.
With this method you would have to create all 40 classes, however there might be a way in Flash when creating a new movie clip to set which class the movie clip extends and then you wouldn't have to create 40 different classes.
Update:
I re-read your question and thought of something else, that option I talked about in the first sentence, the one about Flash giving the option to create a class. Well if a class is not given then Flash will dynamically create a class at run-time. I think when it dynamically creates a class it does not maintain the same name as the library movie clip, so when your trying to pass the static name of your movie clip to your function, it has no clue what your talking about and throws a runtime error.

TweenLite does not work with object

I got the following problem:
I have an object called tempScore for my game.
This object is blitted to the canvas by a renderer via the copyPixels method. The object is NOT a display object. It's a Score-object (self made). The Score-object extends an object called BasicBlitArrayObject. The BasicBlitArrayObject extends an EventDispatcher (therefore no display object).
I tried to apply several different TweenLite-plugins to my tempScore-object (i.e. TransformAroundCenter, colorMatrixFilter, etc.). But nothing happens. Absolutely nothing.
Sometimes I get error messages (when a plugin requires a display object and my object is NOT a display object). So far so good.
According to Greensock (maker of Tweenlite) his engine can tween ANY numeric property of ANY object. So when a plugin like TransformAroundCenter requires a display object for tweening I have to modify the plugin to get it working for my non-display object (tempScore). Currently I can't do that because it's way too hard for me.
My game rests upon this code:
http://www.8bitrocket.com/book/ch11_blastermines.zip
Try to apply TweenLite with an object called tempMine inside the game class BlasterMines. It won't work. Any help, please?
Greensock's claim is correct, in it's exactness. You can tween any numeric property of any object. This statement does not include the application of plugin features.
The reason that the TransformAroundCenter and ColorMatrixFilter plugins don't work for you is that they each utilise some property or method of DisplayObject. In the case of transformAroundCenter that's DisplayObject.localToGlobal() and for ColorMatrixFiler it's DisplayObject.filters.
I have to ask why you're applying these plugins to something that is not a display object? In blitting (as it applies to AS3), the basic idea is that you read an area from a sprite sheet to a BitmapData object, which in turn you write to a Bitmap object on the stage. Both BitmapData and Bitmap extend DisplayObject, which is what you need. For higher compatibility you should target the Bitmap object that is actually on the stage, TransformAroundCenter will not work correctly with an object that is not on the stage.
For a better answer you will have to post some code, and possibly a screenshot from a debugger like Monster Debugger 3 which shows your expanded display tree.