upcasting in actionscript 3 - actionscript-3

Hi I have a custom class that extends Sprite, that controls positioning on the stage. I would like the class that manages the stage to be able to take any sprite that is added to the stage and convert it to custom class type, ie wrap it so that the managing class can position a native sprite accordingly.
Does anyone know if it is possible to do something like the following
var managed:managedSprite = new managedSprite(nativeSprite);
where the managedSprite extends the Sprite class? I don't want to have to composite in a reference to the sprite if at all possible in order to prevent the overhead associated with the managedSprite already extending Sprite.
Is there anyway using reflection (perhaps the commons.reflection library)?
Thanks

You can add an event listener to the stage and use Event.ADDED to get a reference to any display object added anywhere in the display list.
Then simply type cast if the added item is a subclass of ManagedSprite (BTW, the convention is to start your class names with an uppercase letter):
stage.addEventListener (Event.ADDED, onAdded);
function onAdded( ev:Event ):void {
if (ev.target is ManagedSprite) {
var managed:ManagedSprite = ManagedSprite( ev.target );
doStuffWith( managed );
}
}
EDIT
I think I only just understood your question: You are not trying to do an actual type cast - which would require your class hierarchy to be already set up, i.e. you'd have to have extended the ManagedSprite class already - but to add functionality at runtime!
I would strongly discourage you from trying to do deep copies or such - it will be heavy on performance, depending on how many sprites you are going to add, and you will no longer have your compiler to help you prevent errors.
Rather, see if you can't favor composition over inheritance: Create a kind of "proxy" class for the sprite, let's call it ManagedSpriteProxy, which implements all the methods you would call on ManagedSprite, but forwards all the actual manipulations to its `managedSprite' property. Then use the event handler I outlined above to create the proxy objects and attach the respective sprites:
public class ManagedSpriteProxy {
private var _managedSprite:Sprite;
public function ManagedSpriteProxy( managedSprite:Sprite ) {
this.managedSprite = managedSprite;
}
public function get managedSprite():Sprite {
return _managedSprite;
}
public function set managedSprite( managedSprite : Sprite ):void {
_managedSprite = managedSprite;
setUpAnyHandlersOrWhatever();
}
private function setUpAnyHandlersOrWhatever():void {
// Many wonderful things happening
}
// Many more wonderful things happening via public API
}
// somewhere else
public class SpriteManager {
private var _managedSprites:Array = [];
public function SpriteManager() {
addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
}
private function onAddedToStage( ev:Event ):void {
removeEventListener( Event.ADDED_TO_STAGE );
stage.addEventListener( Event.ADDED, onAdded );
}
private function onAdded( ev:Event ):void {
if( ev.target is Sprite ) {
addWithProxy( ev.target as Sprite );
}
}
private function addWithProxy( sprite:Sprite ) : void {
var proxy:ManagedSpriteProxy = new ManagedSpriteProxy( sprite );
_managedSprites.push( proxy );
}
// Insert here whatever methods used to manage the sprites,
// all of them call the proxies instead of the sprites!
}

You may want to use the 'Decorator' pattern.
It seems a little bit complex at the first sight but it's quite easy to understand and use.
http://www.as3dp.com/2009/04/actionscript-30-easy-and-practical-decorator-design-pattern/

Related

Actionscript 3: How do I get all classes using one enter frame function?

I'm making an adventure game in AS3 and have many classes. I heard it is best for performance if there is only one Enter Frame function and all update functions run from that.
I currently have a mainGameLoop in my main.as, what is the best way for all my classes to contain an update function running from this single enter frame function?
Here is an example of what I've done to run an update function in my Player class, but I don't like this solution at all.
If anyone could help it'd be much appreciated, thanks.
public class Main extends MovieClip
{
// Initialising variables...
private var m_SceneHandler:SceneHandler;
private var m_UserInput:UserInput;
public function Main()
{
// Adding main update function...
addEventListener( Event.ENTER_FRAME, mainGameLoop );
// Creating the scene handler...
m_SceneHandler = new SceneHandler();
addChild( m_SceneHandler );
}
private function mainGameLoop( event:Event )
{
doUpdate();
}
private function doUpdate():void
{
// Update player...
if (m_SceneHandler.m_aCurrentScene[0].m_Player)
{
m_SceneHandler.m_aCurrentScene[0].m_Player.doUpdate();
}
}
Use the composite pattern and code to an IUpdateable interface.
package view {
public interface IUpdateable {
function doUpdate():void;
}
}
In your main game loop, just switch out which object(s) should be updated:
package{
public class Main extends MovieClip implements IUpdateable {
protected var updateChildren:Vector. = new Vector.();
//other logic would manage who is currently updateable and who is not
public function doUpdate():void {
for each (var updateable:IUpdateable) {
updateable.doUpdate();
}
}
}
}
Then, if there are subcomponents that need to update, they could also be IUpdateable, and the higher level IUpdateable could call the method.

AS3 - How to get a reference to the container of an aggregated object?

Simple enough.
If I have a container class that holds a Sprite object, and I attach a touch listener to said Sprite, is there a reliable and cheap method of getting the object that contains the Sprite when it is touched? I realize I could just inherit the Sprite, but that is not what I want to do.
Failing that, if I add the event listener to said Sprite object within the class that contains it, is there a way to dispatch an event that would allow me to get the reference to the container that holds the Sprite object that was touched?
Thanks for any help.
Reply to loxxxy:
When I said "held", I meant in terms of aggregation. For example:
public class Container
{
[Embed(source = "img1.jpg")] private var img:Class;
private var sprite:Sprite;
private var bitmap:Bitmap;
public function Container()
{
bitmap = new img();
sprite = new Sprite();
sprite.addChild(bitmap);
}
public function GetSprite():Sprite
{
return sprite;
}
}
Which is perfectly legal code. What I wanted to do was, when the Sprite object is touched outside of the Container class, that I could access other properties within the Container class through said Sprite object. However, a solid workaround would be something like the following, I think:
public class Container extends InteractiveDisplayObject
{
[Embed(source = "img1.jpg")] private var img:Class;
private var bitmap:Bitmap;
public function Container()
{
bitmap = new img();
this.addChild(bitmap);
}
}
Then, I could access the aggregate objects of the Container class by listening to touch events on the Container class, while making it fully extendable to any other DisplayObject class (TextField, Sprite, etc.).
There's a very specific reason I want to do this, I just don't feel it's relevant to the actual question. I'll try this approach when I get some time to test it out, and see how it goes. Thanks!
You don't really need to dispatch events just for this purpose.
Add the event listener to the container & you can get reference to both container & sprite. For eg:
container.addEventListener(MouseEvent.CLICK, container_touched, false, 0, true);
function container_touched(e) {
trace(e.target.name); // Output : sprite
trace(e.currentTarget.name); // Output : container
}
EDIT :
Or you could have rather exposed the sprite event to others by adding a function like :
public function registerCallback( callback:Function) {
var thisRef = this;
sprite.addEventListener(MouseEvent.CLICK, function(e) {
callback(thisRef);
},false, 0, true);
}

AS3: if-function doesn't listen to a boolean in another class

So... I'm working on a chess-game, and trying to make it so that a "public static boolean" (turn) dictates which player can make a move. This boolean is in a class (Board.as) which imports all the classes for all the chess-pieces (e.g. QueenW.as (for the White Queen)).
I've tried multiple ways: Trying to make functions not run anymore, and replacing the pieces (which are buttons) to other objects (non-clickable movieclips). Decided to go with the latter. I've traced the boolean in a chess-piece class, as well as the Board-class, in an ENTER_FRAME function. Both seem to trace it correctly when the value changes.
Problem is: Flash doesn't remove the chess-pieces and replaces them with a non-clickable object, even though the class in which it should happen (Board.as) does listen to the boolean when tracing. Anybody knows a solution?
A little piece of my code, which is relative to the problem:
Board class (which is the Documentclass for my .fla file)
package
{
import QueenWclass; //imports the class used for example.
public class Board extends MovieClip
{
public static var turn:Boolean = new Boolean; //creates public static bool.
var queenW:QueenWclass = new QueenWclass(); //creates aforementioned chess-piece.
var queenWnoturn:QueenWnoturn = new QueenWnoturn; //creates a non-clickable object.
}
public function Board()
{
turn = true;
this.addEventListener(Event.ENTER_FRAME, frameEnter);
addChild(queenW); //adds pieces to the screen.
}
if (turn == true)
{
}
if (turn == false)
{
removeChild(queenW); //Removes chess-piece.
addChild(queenWnoturn); //Adds a non-clickable object.
}
}
And my QueenWclass.as class:
package
{
public class QueenWclass extends MovieClip
{
var queenW:QueenW = new QueenW();
}
public function QueenWclass()
{
addChild(queenW);
this.addEventListener(MouseEvent.CLICK, CLICKqueenW);
}
function CLICKqueenW(event.MouseEvent):void
{
Board.turn = false;
}
}
I hope I wrote this example correctly and understandably. There's no real timelimit to my project as I already had to turn it in an hour ago (but still got a 6/10 because of effort and how far I've come with this rather complex game). I just want to finish it for myself... Thanks in advance!
Maybe the code has not been copied correctly or there is a small problem.
This code:
if (turn == true)
{
}
if (turn == false)
{
removeChild(queenW); //Removes chess-piece.
addChild(queenWnoturn); //Adds a non-clickable object.
}
Will only run once, when "Board" is created, it will not run when the state of "turn" changes.
Well, you have nothing that's listening for the boolean's change. The code that's checking the boolean is located in constructor, while the actual change is done in a MouseEvent.CLICK event listener. You have to either implement a function that's called repeatedly via Event.ENTER_FRAME listening, SetInterval(), or TimerEvent.TIMER (with a timer), or implement a publicly available property as a function, that would check which turn is it and do corresponding actions. The latter is a little better, as it works only when something is changed.
private static var _turn:Boolean=false;
public static function get turn():Boolean { return _turn; } // getter part
public static function set turn(value:Boolean):void // setter part
{
if (_turn==value) return; // no need to change turn
_turn=value;
if (_turn) YouGetATurn(); else EnemyGetsATurn();
// this part is what will get called when you change Board.turn
}

Access function with ENTER_FRAME event from one class and send updates to other class in AS3

I want to start a project. But, while doing a paperwork, i realized one situation as follows,
Say, we have 3 classes.
Main.as,
A.as,
B.as
Here, "Main.as" is a central class which creates instance of A and B.
Class A has some function say "updatePosition(e:Event)" with ENTER_FRAME event.
Class B is required to get update from this "updatePosition(e:Event)" from class A via "Main.as"
How can this be achieved only with one ENTER_FRAME event and that of in A.as class only?
Here's one simple way to do it but I'm sure there are plenty of better ways (such as using custom events, or AS3 signals library):
Main.as
private function eFrame(e:Event)
{
a.runEvents();
b.runEvents();
}
A.as. B.as
public function runEvents()
{
// Run whatever events you need to run
}
This will give you the same effect as 3 ENTER_FRAME events but without the overhead, however you will have overhead from lots of function calls.
Why don't you just put a single ENTER_FRAME handler in main.as and call the updates of the other classes from there?
public class Main extends Sprite
{
private var _a:A = new A();
private var _b:B = new B();
public function Main()
{
if(stage) init();
else addEventListener(Event.ADDED_TO_STAGE,init);
}
private function init(e:Event=null):void
{
removeEventListener(Event.ADDED_TO_STAGE,init);
stage.addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
private function onEnterFrame(e:Event):void
{
_a.updatePosition();
_b.updatePosition();
}
}

AS3 How to extends Event class without keeping any existing event types?

I would like to extends the Event class to add some events I am using in game.
But I don't want the new Event Class to have the old public static types..
For instance I don't want to have:
NewEventClass.ENTER_FRAME
How do you go about extending the Event class without getting the old types mixed in?
Is there any way to outsmart AS3 to leave out the uneeded types?
Or should I avoid creating a new Event type altogether and just add the new strings?
Extending Event is only really necessary if you want to add some extra properties to it, for example:
public class EnemyEvent extends Event
{
// Constants used to represent event type
public static const ENEMY_KILLED:String = "killed";
// Event properties
public var score:int = 0;
/**
* Constructor
* Retain Event behaviours
*/
public function EnemyEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
So that when you dispatch this event from an enemy you can go:
var evt:EnemyEvent = new EnemyEvent(EnemyEvent.ENEMY_KILLED);
evt.score = myScoreValue;
dispatchEvent(evt);
And then make use of the score property from the listening method within the game engine:
enemy.addEventListener(EnemyEvent.ENEMY_KILLED, _countKill);
function _countKill(e:EnemyEvent):void
{
gameTotalScore += e.score;
if(gameTotalScore > 100) getAchievement();
e.target.removeEventListener(e.type, _countKill); // <-- woudn't work without extending event either
}
If you just need to store constants to use in addEventListener(x, ..), new Event(x), etc then you can just make a class that holds these and doesn't have anything to do with Event:
public class CustomEvents
{
public static const MY_CUSTOM_EVENT:String = "myCustomEvent";
}
So that you can just use these as needed:
new Event(CustomEvents.MY_CUSTOM_EVENT);
addEventListener(CustomEvents.MY_CUSTOM_EVENT, _listener);
The former method is still preferable as it's tidier and more logical.
Additionally; your note about your custom event having constants such as ENTER_FRAME isn't the case anyway, because they are static and belong to Event. You'll get this error if you try access ENTER_FRAME through the example in your answer:
1119: Access of possibly undefined property ENTER_FRAME through a
reference with static type Class.