How to address an object through a string? - actionscript-3

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

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!

What happens to a Movieclip when casted as a Sprite?

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

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.

In flash with as3.0, I have to call a function on the main stage from a movieClip

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";

Interaction between classes

In my main Actionscript file i have a instance of a body class that moves the person body arms legs etc. and a gun class which has methods and properties to do with the person gun.
Right now i have a function in the Main class which is called move gun and looks like this and is called everyframe to move the gun to the bodys arm. I was hoping to move this function to the guns class so i could call it like gun.moveGun(); but the body dosent exist inside the gun variable. so i wonder if i could call the body.getArm(); function from inside the gun. I know i could call the function and pass the bodys arm location to it from the main file. But don't know if this is the best way to do it.
private function moveGun():void
{
gun.x = body.getArm('left').x;
gun.y = body.getArm('left').y;
}
It seems like keeping the gun related functions inside the gun class is the best way to organise everything but i dont know how to do this.
Also depending what button someone clicks at the start the person will either have a basketBall or gun in there hand. Ive added the swf online at Here so you can see how it works. i just wanna change how its organised because the main file is very full and though i should learn how to organise things better. There are lots of other part of the program that would be organised better if i knew how to get the object to interact or what is the best way to do it. Starting to think passing the x,y coordinates to the moveGun function inside the gun Class is the best way. If so just tell me please and ill do that.
From the code you posted, I think you need to move the gun with every frame to stick to the player's arm.
The answer you're looking for is Composition, since every person gets one and only one gun, I think you better put a reference (variable) in the person class for its gun.
and here is your player (person) class:
class Person
{
private var m_gun:Gun;
public function Person()
{
this.m_gun = new Gun();
}
// a function which is called each frame
private function updateFrame()
{
// Here you can provide your own logic in Gun class
this.m_gun.doSomething();
}
}
generally, you want to communicate between classes using the built-in event listener and dispatcher system. if 'something' happens - regardless of 'where' that occurs - dispatch an event. whenever something should react, it should have a listener.
for example, from the main class you mentioned, you might want to dispatch an event:
private function moveGun():void
{
var e:Event = new Event('moveGun', true);
dispatchEvent(e);
}
then the appropriate instance (the body? - not sure how you have things set up) listen for that:
body.addEventListener('moveGun', moveGunHandler, false, 0, true);
function moveGunHandler(event:Event):void{
gun.x = body.getArm('left').x;
gun.y = body.getArm('left').y;
}
the above is obviously psuedo-code since i have no way of knowing how your display list is set up. also, i used simple string literal event types - best practice would probably be to use custom event classes with event types defined as static constants.