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

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);
}

Related

access parent movieclip class variable from child/inside movieclip AS3

Maze Runner MovieClip has a "AS Linkage" class MazeRunner
MazeRunner class
public class MazeRunner extends MovieClip
{
public var _startCave :String;
public function MazeRunner():void
{
}
}
and movieclip (Maze Runner>mc_terrain) wants to access _startCave from "MazeRunner" class to be used in "mc_terrain" timeline.
can it be done?
i tried using var mr:MazeRunner = new MazeRunner(); - but it's an error cause i think you can't access your own class/movieclip?
if Maze_Runner is a DisplayObjectContainer and mc_terrain is a DisplayObject attached to Mazerunner via addChild:
var mr:MazeRunner = new MazeRunner(); // you have to have an instance of MazeRunner to run it, anyway
var mt:Terrain = new Terrain();
mr.addChilld(mt);
then you can use
(mc_terrain.parent as MazeRunner)._startCave
to access it.
If they are not attached this way, then you need to reference the MazeRunner inside a mc_terrain (you need a new property for that):
var mr:MazeRunner = newMazeRunner;
mr.mc_terrain.maze_runner = mr;
// ...access:
trace(mc_terrain.maze_runner._startCave);
Lastly, if you always have only one active instance of MazeRunner, you can change its _startCave property to static:
public static var _startCave :String;
This will allow you to modify and read it from any place using static reference:
MazeRunner._startCave = "1";
trace(MazeRunner._startCave);
But it is generally not recommended as it may lead to issues if MazeRunner happens to have several instances which need different _startCave's.

Eventlisteners Not Working On Main Sprite AS3

I have been looking around at a few articles and SO questions to get a grasp on what exactly I am experiencing with my project, but have not found an answer.
I am creating a main sprite that acts as my root for this particular project (I am using terminology as I currently understand it. If I am in error please correct me). In that sprite class, I add a MouseEvent listener so that I can operate in some way with the mouse. The problem is, the main sprite acts as if it either can not read my mouse events or it has no size and therefore can not register mouse events. Here are my classes and the tests I have preformed.
============
Main Class =
============
package
{
import flash.display.Sprite;
import Fu;
[SWF(backgroundColor = "0xffffff", width = "550", height = "400")]
public class Main extends Fu
{
public function Main():void
{
super();
addMouseListener();
}
}
}
==========
Fu Class =
==========
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Fu extends Sprite
{
private var display_height:uint;
private var display_width:uint;
private var default_background_color:uint;
public function Fu(display_height:uint = 400, display_width:uint = 550, default_background_color:uint = 0x2e2e2e)
{
this.display_height = display_height;
this.display_width = display_width;
this.default_background_color = default_background_color;
if (stage) {
init();
}
else {
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
graphics.beginFill(default_background_color, 1);
graphics.drawRect(0, 0, display_width, display_height);
}
public function addMouseListener():void {
trace("Added Mouse Listeners");
addEventListener(MouseEvent.CLICK, onMouseEvent);
// Also tried MouseEvent.MOUSE_OVER, MouseEvent.ROLL_OVER
// with no results either.
}
private function onMouseEvent(e:Event):void {
trace("Mouse Event Fired.");
}
}
}
Everything compiles ok and runs just fine until I try to activate a MouseEvent such as a click, in which case, nothing is output to the console.
================
Console Output =
================
Added Mouse Listeners
//Event messages should appear here...
If I then add a second sprite (s) to the Main class with the following code...
===========================
Added to Main constructor =
===========================
var s:Sprite = new Sprite();
s.name = "s";
s.graphics.beginFill(0x000000);
s.graphics.drawRect(0, 0, 200, 200);
addChild(s);
and click on s I get the "Mouse Event Fired." in the console window, but not when I click outside of s' 200x200 box.
I also tried adding the listener to the stage (which is not ideally what I want since I want these listeners to only affect the main sprite of this specific SWF file) and tracing out...
trace(e.target, " ", (e.target as DisplayObject).name);
The areas not covered by sprite s display "[object Stage] null" in the console and "[object Sprite] s" is displayed when s is clicked.
An obvious fix would be to use s as my "stage" and call it a day, but I an curious why event listeners do not work on the main sprite and why it does not have a default instance name like other sprites do.
Any ideas?
You document class cannot extend Fu since Fu require two parameters and a document class cannot have one. Besides it makes no sense for a document class to extend anything other than Sprite or MovieClip since there cannot be any object higher in the hierarchy of your app. But if you have to extend a custom super class that super class cannot take parameter. Instead create a separate init method that you call explicitly yourself to register your events and draw your content.
Document classes are a special case. When used as the main entry in your app (as opposed to an externally loaded swf), they are set to not receive interactive event directly and behave much like a Singleton. They are also set as the root of your app and are the only DisplayObject that has the stage directly available in constructor. The design idea is that it should be a container for your app and not a part of it directly. The hierarchy is stage -> root (document class) -> your app elements.
In your case for example if you were to add a Fu instance as a child of the document class instance then it will receive mouseevent normally and the document class would also catch those event as they propagate.

upcasting in 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/

AS3: Access my Main.as from a movieclip on stage

Using Adobe Flash Professional CS6, AS3
Please let me know if I need to provide any more info
I am trying to set up a navigation menu and my question is, "How do I get a MovieClip to call a function in my Main.as file from the Stage?" There is only one frame (I do not want more) and the different menu screens, which are just MovieClips, are added to the stage with AS when needed. I have a public function in the file Main.as called _About(), that my MovieClip, "MenuScreen", cannot access. I can successfully have _Menu() add a MovieClip to the stage with eventListeners, but when the mc makes the call back to change screens I get this error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.display::Stage#51ca0d1 to flash.display.MovieClip. at MenuScreen/clickAbout()[MenuScreen::frame1:32]
Frame1:32's code is:
MovieClip(parent)._About();
Line 51 in my Main.as is:
public function _About():void
{
trace("The About Function");
}
Below I have detailed more about Main.as with most of the fat trimmed.
package
{
import stuff
public class Main extends MovieClip
{
//Load the screen MCs onto the stage
public var _menu:MenuScreen = new MenuScreen();
public var _about:AboutScreen = new AboutScreen();
public var isMenu:Boolean = true;
public var isAbout:Boolean = false;
public function Main()
{
_Menu();
}
public function _Menu():void
{
isMenu = true;
stage.addChild(_menu);
}
public function _About():void
{
trace("The About Function");
}
An easy solution to your problem would be to add the menu items not to the stage! Instead add them to your main class. This way the parent of your items is instead main.as
But then you need to cast the parent to Main
Main(parent)._About();
Also not very nice. The items should not now what is behind them.
The best way is to do it, is to dispatch events from the different screens.
Means: you create your screen objects an there are dispatching custom events when a screnn change should happen.
dispatchEvent(new Event("showAbout"));
in your main class you handle the events like:
public function Main()
{
_Menu();
_menu = new MenuScreen();
_menu.addEventHandler("showAbout", showAboutHandler);
}
public function showAboutHanlder(e:Event):void
{
_About();
}
Even more better is a custom event with a screen identifier as a param. This way you just add one handler and decide in the handler code which screen to be displayed.
With the event handling in place, your menu items have no direct connection to the main. Also the main needs no further information about the screen classes.
Set static property :
public static var instance:Main;
public function Main(){
instance = this;
_Menu();
}
and then from anywhere You can use code :
Main.instance._About();
It would help to see MenuScreen's complete class, but here's what's probably going on:
You're instantiating at public var _menu:MenuScreen = new MenuScreen(); and when this happens it's probably making the call to MovieClip(parent)._About() before you've added MenuScreen to the stage at stage.addChild(_menu); - It doesn't have a parent yet when this occurs and the error incurs.
Two ways to get around this:
Add a required parameter in MenuScreen's constructor that references your Main class. Your constructor in MenuScreen would start with public function MenuScreen($main:Main){ and when you instantiate MenuScreen from Main class you would write public var _menu:MenuScreen = new MenuScreen(this); now you can use $main._About() in MenuScreen's constuctor.
In MenuScreen's constructor add a listener that checks when it's been added to the stage: addEventListener(Event.ADDED_TO_STAGE, addedStage). In the function addedStage you can properly call for a parent or the stage, and MovieClip(parent)._About() will probably work here.

Controlling objects of another class that are already on the stage

I'm quite ashemed to ask this question here because I'm sure that I'm missing something very basic. I'm not even sure what should be the correct title for this question.
Let's say that I've a button object (instance of Flip) and a coin object (instance of Coin) on the stage. The coin object has two frames: one showing Heads and one for Tails.
MyCoin class is as following:
package
{
import flash.display.MovieClip;
public class Coin extends MovieClip
{
protected var _coinFace:uint;
public function Coin()
{
stop();
}
public function get coinFace():uint {
return _coinFace;
}
public function set coinFace(value:uint):void {
_coinFace = value;
}
public function show():void {
gotoAndStop(_coinFace);
}
}
}
Objective: When user clicks the button, the coin should flip and show a random coinFace. I've added an eventListener to the Flip class as follows:
public function Flip()
{
this.addEventListener(MouseEvent.CLICK, onMouseClick);
}
Problem: How do I reach the coin object on the screen via onMouseClick function? Let's say that the object on the stage has instance name of myCoin. I suppose that had I not done this with an external class and simply used actions from the frame I could just use the instance name as a variable. I couldn't figure to do the same it in an external class. Do I first create the object which is already on the stage?
Where you create the instance of each, the flip object needs to be passed an instance of the coin object.
var myCoin:Coin = new Coin();
var myFlip:Flip = new Flip(myCoin);
Then inside the Flip class:
private var _coin:Coin;
public function Flip(coin:Coin) {
_coin = coin;
this.addEventListener(MouseEvent.CLICK, onMouseClick);
}
private function onMouseClick(e:MouseEvent):void {
_coin.gotoAndStop(2); // Or what ever needs to be done to the coin on click
}
Alternatively, depending on the complexity of the overall structure, you can a build a control class that acts as a link between the two.