How to access the variable "abc" in the following code structure - actionscript-3

1) First of all I don't wanna use CustomEvent class. Some solution I am looking without using CustomEvent.
2) One of the solution can be having abc variable in ClassA. And then dispatching directly via ClassA ( rathar than saying classB.dispatchEvent() ). But still looking if there is some better solution than this.
//Frame1 code :
import flash.events.Event;
var classA:ClassA = new ClassA() ;
classA.addEventListener("hello", hello);
classA.init();
function hello(e:Event)
{
trace(e.currentTarget.abc); //<<<< NEVER EXECUTED
}
//classA
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class ClassA extends MovieClip
{
var classB:ClassB ;
public function ClassA()
{
classB = new ClassB();
}
public function init()
{
classB.dispatchEvent( new Event("hello"));
}
}
}
//classB
package
{
import flash.display.MovieClip;
public class ClassB extends MovieClip
{
public var abc:Number =123;
public function ClassB()
{
}
}
}

You are missing a couple key concepts before you can get your example to work. First you are dispatching the event on an instance of ClassB, however you are listening on an instance of ClassA. So, they have to be related in some way, in order for event to be properly orchestrated when it gets dispatched. One way to do that is to use event bubbling. One caveat to that is that native event bubbling only really works for DisplayObjects, but both of your classes inherit from MovieClip so thats not a big deal.
So the first thing, you have to understand how bubbling events work. A simplified explanation is that events start at the top of the display hierarchy and capture down the display tree towards the element, they are finally dispatched on the target, then they turn around and bubble back out in the opposite direction.
This means that your instance of ClassB has to be a child of ClassA. So the first thing you'll have to change is in your ClassA constructor:
public function ClassA()
{
classB = new ClassB();
addChild(classB);
}
Next, when you dispatch the event, you'll need to explictly say that its a bubbling event, otherwise it'll be triggered on the target, and neither capture nor bubble through the display stack.
public function init()
{
classB.dispatchEvent( new Event("hello", true));
}
The second argument of true sets the event to a bubbling event.
Finally you'll need to change your handler. Right now, it's using e.currentTarget, which isn't going to be what you expect in this case (usually it is, thought).
You have to understand the difference between e.target and e.currentTarget. e.target is the actual target of the event, independent of how its bubbling or capturing. e.currentTarget on the other hand is the element which is presently handling the event. So in your case e.currentTarget is an instance of ClassA (the instance that the event handler is actually attached to) and e.target is an instance of ClassB (the instance that the event was dispatched on). During the event lifecycle, e.currentTarget will change as the event moves around, but e.target should always be the same.
In this case, you want to reference the actual target of the event, not the element that is currently processing the event. So you need to change your handler to:
function hello(e:Event)
{
trace(e.target.abc);
}
And then it should work. You can find a working example here that encapsulates the changes I've described.
If these classes weren't DisplayObjects then you would have to take a different approach -- either by using a signal pattern or to manually listen for an retrigger the event inside ClassA.

First of all you are adding an event listener to classA but your classA init method is asking classB to dispatch an event and this is the reason why your code does not get executed. If you want to catch the hello event you should be doing something like
public function init()
{
this.dispatchEvent( new Event("hello"));
}
Or you should be registering the listener on classB (which is not in scope so no code suggestion).
In ActionScript the best approach to transfer information is to use custom events so my suggestion is to re evaluate your decision on custom events.

Related

Custom Event Listener not working

I am new to as3 and I recently saw creation of custom events in as3 in a tutorial and I wanted to incorporate in my game. When i did the tutorial, it all seemed well for that project. But it doesnt seem to work with the new project.
Here is my code :
package
{
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class FashionFrenzy extends MovieClip
{
public var Buyer_mc:Buyer;
public var Buyers:Array;
public var gameTimer:Timer;
public function FashionFrenzy()
{
GameTimeController();
GenerateBuyers();
addEventListener(ReachMallDoorEvent.CHECK, OnReachMallDoor);
}
public function GameTimeController()
{
gameTimer = new Timer( 25 );
gameTimer.start();
}
public function GenerateBuyers()
{
Buyers = new Array ;
Buyer_mc = new Buyer(533.2,0) ;
addChild(Buyer_mc);
gameTimer.addEventListener( TimerEvent.TIMER, BuyerEnter );
if(Buyer_mc.y==377.25)
{
dispatchEvent( new ReachMallDoorEvent( ReachMallDoorEvent.CHECK ) );
}
}
public function BuyerEnter(event:TimerEvent)
{
Buyer_mc.Enter();
}
public function OnReachMallDoor(event:ReachMallDoorEvent)
{
trace("my timer starts now");
}
}
}
Here, OnReachMallDoor never seems to run because there is something wrong. I cant see the output saying "My timer starts now". But there is no error in the code and output doesnt show any runtime errors either. Where have I gone wrong? I want OnReachMallDoor function to run when my y coordinate is in desirable position and the event is dispatched.
The order of commands is wrong.
GenerateBuyers();
addEventListener(Rea...
The first line of these two is the one that could potentially cause the Event to be dispatched. But only after that will you start listening for it. That's simply too late. You have to start listening before the Event is dispatched.
The probability of the Event to be dispatched is very low.
Buyer_mc.y==377.25
Checking floating point values for equality is often not a good idea. It could easily be just slightly off due to rounding errors etc. If this .y property was controlled by the mouse for example, you'd have to position the mouse at exactly that position, which is very unlikely.
You only dispatch the Event at the beginning.
GenerateBuyers();
That function is only called once. The .y position is evaluated.
This only happens once and never again.
But the .y position is subject to change and the condition should be evaluated again, which doesn'T happen
The structure is not helpful.
It doesn't make much sense for an object to listen for its own Events. Simply call the function and be done with it.
Events are for communication between objects.
How this is supposed to be:
The point of the custom Event is to be notified about something.
You want to be notified when this condition
Buyer_mc.y==377.25
is true.
If you are evaluating that condition the way you do it now, then there's no point in receiving a notification about the result thereof. You have it already.
Instead, Buyer_mc should dispatch the Event. The condition should be checked in Buyer class.
What the code looks like
some snippets pointing out what the above means, code untested:
class Buyer
override public function set y(value:Number):void
{
if (value == 377.25)
dispatchEvent(new ReachMallDoorEvent(ReachMallDoorEvent.CHECK)); // I'm at the position, I send out the notification
super.y = value;
}
class FashionFrenzy
buyer = new Buyer(533.2, 0); // variables should start with small letter
buyer.addEventListener(ReachMallDoorEvent.CHECK, OnReachMallDoor);
If you now set the .y position to the value, the object will dispatch the Event. It will figure that out on its own.
Letting the object figure something out on its own and just receive a notification about it is the main reason to use custom events.

Troubles with Custom Events

I've been having lots of trouble with custom events in as3 recently, I've googled a bunch of stuff that doesn't help and used this tutorial to try to figure them out but I'm still confused.
So I set up a new flash fla file to test them and couldn't figure it out, here is what I have:
document as file:
package
{
import flash.events.MouseEvent;
import flash.display.MovieClip;
public class Custom extends MovieClip
{
var intializer:Initializer;
var added:Added;
public function Custom()
{
intializer=new Initializer();
addChild(intializer);
intializer.addEventListener(MouseEvent.CLICK, OnClicker);
addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch);
}
private function OnClicker(event:MouseEvent):void
{
added=new Added();
added.x=300; added.y=300;
addChild(added);
}
private function OnCatch(event:CustomEvent):void
{
trace("hi");
removeChild(added);
}
}
}
event as file:
package
{
import flash.events.Event;
public class CustomEvent extends Event
{
public static const EVENT_CUSTOM="event1";
public function CustomEvent(type)
{
super(type, false, false);
}
}
}
and the movieclips as file:
package
{
import flash.events.MouseEvent;
import flash.display.MovieClip;
public class Added extends MovieClip
{
var addedButton:AddedButton;
public function Added()
{
addedButton=new AddedButton();
addedButton.x=30; addedButton.y=30;
addChild(addedButton);
addedButton.addEventListener(MouseEvent.CLICK, OnClickie);
}
private function OnClickie(event:MouseEvent):void
{
dispatchEvent(new CustomEvent(CustomEvent.EVENT_CUSTOM));
}
}
}
Buttons use an empty class, This gives me this result: (top left corner for first button.)
http://www.fastswf.com/_EfGSoQ
Sorry for so much code, but custom events seem to require a lot of code.
The problem seems to be that you are listening for your custom event in the wrong place. You can address this several ways using event bubbling, the event's capture phase, or by listening for the event on the object that dispatches the event (the event target).
The Flash Event Model follows the W3C event model (used in the DOM/Javascript). When an event is dispatched, it goes through three phases: capture phase, target phase, and bubbling phase. It's described in the above link (in the "Event propagation and phases" section).
For performance reasons (my assumption), the capture and bubbling phases are not enabled by default.
Note you only need to do one of the 3 things below. I suggest using the event target, it's the easiest to understand. The others have their places and can be very useful, but you can do most everything w/the target phase.
How to use the target phase
In your document class remove this line from the constructor:
addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch);
Remove it because we're going to listen for the event coming from the Added object directly (it's the event target). To do that change your code that adds the object in the document class:
private function OnClicker(event:MouseEvent):void
{
added=new Added();
added.x=300; added.y=300;
addChild(added);
added.addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch);
}
How to use the capture phase
In your document class, add an additional parameter when adding the event listener to enable the capture phase:
addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch, true);
This allows any parent of the event target to handle the event, before the target handles it.
How to use the bubbling phase:
To use the bubbling phase, your custom event object needs to "bubble". So you will need to modify the constructor in your custom event class:
public function CustomEvent(type)
{
super(type, true, false);
}
Here I've changed the second parameter in the call to super(), allowing the event to "bubble".
Since the event now bubbles back up the display list, any parent of the event target will also be able to listen for the event after the target has handled it.

Overriding flash.display.Sprite's eventDispatcher

Hai,
I want to override the dispatchEvent method while inheriting from flash.display.Sprite. Whenever an event gets dispatched now like ADDED_TO_STAGE and CLICK it will get through dispatchEvent in my theory. However there is no single trace he'll get through dispatchEvent.. so the events get dispatched internally from some where else?
public class TestSprite extends Sprite
{
public function TestSprite()
{
this.addEventListener(Event.ADDED_TO_STAGE, this.handleAddedToStage);
}
private function handleAddedToStage(event:Event):void
{
}
override public function dispatchEvent(event:Event):Boolean
{
trace(event.type);
return super.dispatchEvent(event);
}
}
this._sprite = new TestSprite();
this._sprite.graphics.beginFill(0x000000);
this._sprite.graphics.drawRect(20, 20, 200, 200);
this._sprite.graphics.endFill();
this.addChild( this._sprite );
There is no trace..
The Sprite implements IEventDispatcher so that the coder - you - can dispatch custom events from display-list classes. As you suspected however, native Flash Player events are not dispatched through the dispatchEvent method itself, they are created and passed to event listeners internally. I imagine a primary reason for this is performance.
sooner or later in the function you need to pass the event to the real sprite eventDispatcher or it won't work (hint: super.dispatchEvent(event) )
by the way this will work only on objects that subclasses your own class, the others will continue to subclass Sprite, hence use the Sprite method.

as3 - dispatchEvent from a parent swf to a child swf

I have one main "parent" swf that loads several other swfs. If something happens in the main swf I need to tell one of the child swfs about it.
This seems to work well the other way around. Any of the children can simply dispatchEvent(), and I can set up the main swf to listen for the event. However, I can't get the child swf to catch any events dispatched by the parent. How is it done?
OK, so if you know most of this already, my apologies... but it seems a pretty common issue and isn't immediately obvious.
In AS3 events dispatched by objects on the display list can be listened for as they bubble up the display list hierarchy without needing to specify the originating object. (Assuming of course that the event has its bubbling property set to true). Hence the Document Class (the old concept of _root) can respond to mouse clicks from any display object, no matter how deeply nested, with addEventListener(MouseEvent.CLICK, _onMouseClick)
In any other situation - e.g. bubbling is set to false, the broadcaster is not an InteractiveObject on the display list or, (as in your case) the listener is lower than the broadcaster in the display list hierarchy - the object broadcasting the event must be specifically listened to: fooInstance.addEventListener(Event.BAR, _bazFunc) as opposed to just addEventListener(Event.BAR, _bazFunc)
Basically you need to pass a reference to the parent object to your child swf so that it can then attach event handlers to it.
One method is to dispatch an event from the child to the parent class via the display list (once the child has loaded and fully initialised). The parent uses the event.target property of this event to reference the child and set a parentClass variable on it. This can then be used to attach listeners:
package {
class ChildClass
{
private var __parentClass:ParentClass;
// EventID to listen for in ParentClass
public static const INITIALISED:String = "childInitialised";
// Constructor
function ChildClass()
{
// Do initialising stuff, then call:
_onInitialised();
}
private function _onInitialised():void
{
// Dispatch event via display hierarchy
// ParentClass instance listens for generic events of this type
// e.g. in ParentClass:
// addEventListener(ChildClass.INITIALISED, _onChildInitialised);
// function _onChildInitialised(event:Event):void {
// event.target.parentClass = this;
// }
// #see mutator method "set parentClass" below
dispatchEvent(new Event(ChildClass.INITIALISED, true);
}
public function set parentClass(value:ParentClass):void
{
__parentClass = value;
// Listen for the events you need to respond to
__parentClass.addEventListener(ParentClass.FOO, _onParentFoo, false, 0, true);
__parentClass.addEventListener(ParentClass.BAR, _onParentBar, false, 0, true);
}
private function _onParentFoo(event:Event):void
{
...
}
}
}
Dispatching a custom ChildSWFEvent - i.e. instead of using a class-defined constant as above - will make this a more flexible solution since the ParentClass instance can listen for a common ChildSWFEvent.INITIALISED event broadcast by any child swf with contextually useful information passed as an additional parameter.
When you load a child swf (Main.swf) in an parent swf (Index.swf), keep a reference in a field variable or class variable
fldMain = BulkLoader.getLoader("Index").getContent("Main.swf") as DisplayObject;
this.addChild(fldMain);
(i'm using BulkLoader to load any content)
It's a good practice to wait with dispatching events until the child is added (ADDED_TO_STAGE event)
When I want to dispatch an event to my child I just say:
fldMain.dispatchEvent(new CustomEvent(CustomEvent.INIT_CHILD,data));
What I did was add a listener on the parent for changes after the child is added to the stage. Now anytime you want to have children deal with updating themselves, just dispatch the Event.CHANGE from the parent. Bubbling can be true or false.
I would think that if you attach the child;s listener to the Stage (stage.addEventListener...) any object that throws a Event.CHANGE could trigger the child to handle the event.
package
{
import flash.display.*
import flash.events.*
public class Child extends Sprite
{
public function Child():void
{
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
}
private function addedToStageHandler(pEvent:Event):void
{
trace("CHILDADDED");
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
this.parent.addEventListener(Event.CHANGE, parent_changeEventHandler, false, 0, true);
}
private function parent_changeEventHandler(pEvent:Event):void
{
trace("PARENT_CHANGED");
}
}
}
IMO, it is almost never appropriate for a child to know or care about its parent. On the other hand, parents nearly always know everything about their children (since they have a direct reference to them). So, in this case, I would simply create a property or method on the child Class that could be set/called by the parent when needed.
This has the advantage of better performance, since creation and handling of an Event is more expensive than simply calling a method or setting a value.
HTH;
Amy
I would listen in each child for
Event.ADDED_TO_STAGE
once it has been added to the stage, you can then reference/listen to the stage for events.
Example
//Child
if(stage) _init(); //Already added
else addEventListener(Event.ADDED_TO_STAGE, _init); //waiting to be added
private function _init(e:Event = null):void
{
stage.addEventListener(CustomEvent.CHANGED, _onStageChanged);
{
I didn't test this, but as long as you dispatch the events from the stage, it should work.
//stage
dispatchEvent(new CustomEvent(CustomEvent.CHANGED));
if you setup your custom event class correctly you can also pass information accross.

adding a custom event listener in as3

I've done a lot of reading through forum posts and tutorials, but I still can't wrap my brain round events and event listeners. I have a pretty simple example, but I can't get it to work.
I have an arrayCollection of custom objects in a repeater, when one of those objects is clicked, I want a different componenet to display data associated with that object.
Here's what I have, but the listener never responds (the dispatcher seems to be working though, because the new event is created and I can see the trace with the proper output.) I suspect it is because when I call addEvent Listener, I am doing so on the wrong object. My understanding is that the object that will display the object data is the object that should have the event listener, and listen for all events of this nature, but maybe I misunderstood.
My custom event:
public class SelectObjectEvent extends Event
{
public function SelectObjectEvent(customEventString:String, myObject:customObject)
{
super(customEventString, true, false);
trace(customEventString+" "+myObject);
}
}
}
My custom object has the following function which is called on click:
public function selectObject(myObject:customObject):void
{
dispatchEvent(new SelectObjectEvent("OBJECT_SELECTED", customObject));
}
And the component I want to display the selected object has the following constructor:
public function SelectedObjectDisplayClass()
{
addEventListener("OBJECT_SELECTED", this.showObject)
}
public function showObject(event:Event):void
{
trace("Show object: "+event);
}
It's not quite clear where your last two code chunks are, but it looks like you need to be calling addEventListener on the object that extends EventDispatcher.
That is, if your second chunk belongs to a custom object called Clickable, which extends EventDispatcher and calls dispatchEvent() when clicked, then your component should be calling myClickable.addEventListener(...) where myClickable is an instance of Clickable. Does that make sense?
But assuming your 3rd code chunk is not in the same class as the second, it doesn't look like you're doing that. You're adding a listener to the class that owns the third chunk of code, which I gather is not the one that extends EventDispatcher.
Just a quick glance at your code and notice that your dispatchEvent second parameter is the class not the instance of the object. Shouldn't this be myObject?
public function selectObject(myObject:customObject):void
{
dispatchEvent(new SelectObjectEvent("OBJECT_SELECTED", **customObject**));
}