I read a lot of topics about dispatching event but I can't make my code work.
This topic and this topic are closed to what I would like to do, but it's not working in my case.
Here is the situation :
My scene is a battle field and has two ships
Each ship knows when it is touched by a fire, so it has to inform the scene that contains the graphic interface
So the ship dispatch a custom event with itself as parameter, so the scene knows when a ship is touched and which ship
I have 3 classes :
The custom event class is an event that has a property "Ship"
The EventDispatcher class
The symbole class that corresponds to my scene and listen to the event
1) CustomEvent class
public class FightEvent extends Event
{
public static const SHIP_TOUCHED:String = "SHIP_TOUCHED"; //type
public var object:Ship = null; //object to pass
public function FightEvent(type:String, pObject:Ship, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
object = pObject;
}
public override function clone():Event
{
return new FightEvent(type, object, bubbles, cancelable);
}
}
2) EventDispatcher
public class Ship extends EventDispatcher
{
private function updateDamages():void
{
//compute damages
dispatchEvent( new FightEvent( FightEvent.SHIP_TOUCHED, this ) );
}
}
3) Scene
public class Fight extends customMovieClip
{
private var playerShip:Ship; //I have two ships, player and enemy
private var enemyShip:Ship;
public function init():void
{
stage.addEventListener(FightEvent.SHIP_TOUCHED, onShipTouched);
//I made a test : the event listener is correctly added
}
private function onShipTouched(e:FightEvent):void
{
//update the graphic interface to show damages
}
}
My event listener is added, the code passes on the dispatch line, but onShipTouched is not called.
Please help, what did I miss ?
What is the element I didn't understand ?
Is it a good way to use events like this ? or should I set a reference to the scene inside the Ship class ?
Your Ship class is not a MovieClip, Sprite or their subclass in order for your event to bubble up the display list, just because they don't participate in a display list. So, you change your Ship to Sprite subclass (this one already inherits EventDispatcher), add graphics and addChild() both ships to the stage, this way your stage will receive events properly.
My logic was wrong :
I was doing this : the eventdispatcher is dispatching the event, and the stage is listening to the event. That is incorrect.
The right solution is : the eventdispatcher is dispatching the event, and is also listening to its own dispatched event. The eventlistener is added to the eventdispatcher in the scene, that contains the function to call.
As Vesper said, the parameter in FightEvent is useless, as the eventdispatcher is actually the event target.
DodgerThud, BotMaster, you gave me this answer, thank you for your help (Looks like I can't set a comment as "answer accepted").
Here the right code for the Fight class :
public class Fight extends customMovieClip
{
private var playerShip:Ship; //I have two ships, player and enemy
private var enemyShip:Ship;
public function init():void
{
enemyShip.addEventListener(FightEvent.SHIP_TOUCHED, onShipTouched);
playerShip.addEventListener(FightEvent.SHIP_TOUCHED, onShipTouched);
}
private function onShipTouched(e:FightEvent):void
{
//e.target == the ship that dispatched the event
}
}
Related
I have a class "Shop" through which I'm trying to dispatch an event. Previously, I've done with another class "Buyer" and it works but the same thing doesn't work for my new class.
Function of Shop Class that is dispatching event:
public function Select(event:MouseEvent):void
{
trace(ShopName);
dispatchEvent( new SelectEvent(SelectEvent.SHOPSELECT,null,this));
}
The code when I run it , displays the "Shop Name" efficiently through trace but it doesn't seem to dispatch the event or else, it is dispatched but not reccieved well.
Reccieving code for Shop class dispatched event:
addEventListener(SelectEvent.SHOPSELECT,shopselected,true);
Buyer Class' function where the same method worked:
public function Select(event:MouseEvent):void
{
trace(buyerCode);
if(y>=377|| enteredmall)
{
dispatchEvent( new SelectEvent(SelectEvent.BUYERSELECT,this,null));
}
}
Recceiving Code for the Buyer Class dispatched event:
addEventListener(SelectEvent.BUYERSELECT,showselectbuyer,true);
This works but Shop class' one doesn't.
The only difference between both the classes is that the CLICK is on different objects. The Buyer class's object when clicked dispatches the event but the Shop Class's movieclip when clicked dispatches the event.
I've learnt about bubbling of events and I think I'm doing it right in the code. I've read similar question that was asked here but I couldn't understand how to proceed with the solution as the answer was not clear to me.
How to make it work for the shop class as well?
Additional Information:
Select Event Class:
package
{
import flash.events.Event;
import flash.utils.getQualifiedClassName;
public class SelectEvent extends Event
{
public static const BUYERSELECT:String = "buyerselect";
public static const SHOPSELECT:String = "shopselect";
public var selectedbuyer:Buyer;
public var selectedshop:Shop;
public function SelectEvent(type:String, selectedbuyer:Buyer=null, selectedshop:Shop=null, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type,bubbles,cancelable);
if(selectedshop==null)
{
this.selectedbuyer= selectedbuyer;
}
else
{
this.selectedshop= selectedshop;
}
}
override public function clone():Event
{
return new SelectEvent (type, selectedbuyer, selectedshop ,bubbles, cancelable);
}
}
}
I have a fla file and 2 class files. On my fla I have:
addEventListener(SubtitleLoadEvent.PASS_PARAMS, onProcessedEvent);
function onProcessedEvent(e:Event):void {
trace(e.currentTarget);
}
SubtitleLoadEvent.as :
package
{
import flash.events.Event;
public class SubtitleLoadEvent extends Event
{
public static const PASS_PARAMS:String = new String("passparams");
public var resultArr:Array = new Array();
public function SubtitleLoadEvent(type:String, arr:*, bubbles:Boolean = false,
cancelable:Boolean = false):void
{
this.resultArr = arr;
super(type, bubbles, cancelable);
}
override public function clone():Event
{
return(new SubtitleLoadEvent(type, resultArr, bubbles, cancelable));
}
}
}
And I have a class file which extends sprite :
dispatchEvent(new SubtitleLoadEvent(SubtitleLoadEvent.PASS_PARAMS, cleanArr));
But the movie doesn't output anything. How can I fix this?
Since your event doesn't bubble, the only way your timeline code will hear the event is if it was dispatched on the same scope (which is unlikely to be the case here).
If your dispatching sprite is on the same scope (timeline) or a descendant/child of it, then making the event bubble (third parameter when creating the event) should make it work. (or you could listen on the capture phase)
Otherwise, you will need to listen for the event on some common parent of both objects.
The easiest way to resolve this, is to dispatch and listen on the stage:
stage.addEventListener(SubtitleLoadEvent.PASS_PARAMS, onProcessedEvent);
stage.dispatchEvent(new SubtitleLoadEvent(SubtitleLoadEvent.PASS_PARAMS, cleanArr));
This assumes that your dispatching sprite has been added to the display list. If not, then the stage property will be null. If it's not on the display list, then it will not work.
TO learn more about how events work and their lifecycle, you could read this article.
In the code below, the event "myEvent" dispatched in class A is never listened in class C. Do you see where the problem is?
Class A extends EventDispatcher{
A(){
dispatchEvent(new Event("myEvent"));
}
}
Class B extends A{
...
}
Class C extends EventDispatcher{
method(){
addEventListenet("myEvent", onresult);
var ob:A = new B();
ob.method();
}
onResult(){
...
}
}
The problem here is, your A object in creation does not know wherer to dispatch the event "myEvent", and dispatching to self does not do a thing, because there is nowhere for the event to go, even if you'll tell it to bubble. You need to provide a listening object to either the entire A class, or to a specific object of type A, so that that object will be the source of dispatchEvent() call, becoming the target for the dispatched event.
class A { // no inheritance needed
public static var listener:EventDispatcher;
public function A() {
if (listener) listener.dispatchEvent("myEvent");
}
}
Another possibility is to create a static class just for the purpose of sending broascast events, implement its functionality as you would do with an EventDispatcher (use internal instance of EventDispatcher to add and remove listeners, dispatch events and check if there's someone listening), and use that class's interface to register with any event type that you're intending to send to all who will listen.
public class Broadcaster {
private static var _instance:EventDispatcher=new EventDispatcher();
public static function addBroadcastListener
(s:String,f:Function,c:Boolean=false,p:int=0,w:Boolean=false) {
if (_instance) _instance.addEventListener(s,f,c,p,w);
}
// etc, just make wrappers for EventDispatcher functions
}
Then in C.method you do:
Broadcaster.addBroadcastListener("myEvent",onresult);
And in A() you do:
Broadcaster.dispatchEvent(new Event("myEvent"));
You need to attach listener to the the ob:
Class C extends EventDispatcher{
method(){
var ob:A = new B();
ob.addEventListener("myEvent", onresult);
ob.method();
}
onResult(){
...
}
}
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();
}
}
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.