How do I modify existing AS3 events so that I can pass data? - actionscript-3

So I want a way to set up events so that I can pass data without creating closures \ memory leaks. This is as far as I have got:
package com.events {
import flash.events.Event;
public class CustomEvent extends Event {
public static const REMOVED_FROM_STAGE:String = "removedFromStage";
public var data:*;
public function CustomEvent(type:String, customData:*=null, bubbles:Boolean=false, cancelable:Boolean=false) {
super(type, bubbles, cancelable);
this.data = customData;
}
public override function clone():Event {
return new CustomEvent(type, data, bubbles, cancelable);
}
public override function toString():String {
return formatToString("CustomEvent", "type", "data", "bubbles", "cancelable", "eventPhase");
}
}
}
This gets me the following behavior:
function testme(e:Event) {
trace(e);
}
test_mc.addEventListener(CustomEvent.REMOVED_FROM_STAGE, testme);
test_mc.dispatchEvent(new CustomEvent(CustomEvent.REMOVED_FROM_STAGE, 42));
//Traces [CustomEvent type="removedFromStage" data=42 bubbles=false cancelable=false eventPhase=2]
removeChild(test_mc);
//Traces [Event type="removedFromStage" bubbles=false cancelable=false eventPhase=2]
My goal is to get the custom data I want to pass to get passed from the event flash fires, not just the one that I fire. For example, what if I wanted to pass a movieclip along with a loader.COMPLETE event to put the resulting bitmap in?

You extended the Event class for it to dispatch with extra data, now if you want the Loader class to dispatch your custom event type, extend the Loader class to do that (or any other class you want to do this with). In this example I'll override URLLoader with this functionality (because Loader actually dispatches events from it's contentLoaderInfo, which needs two overridden classes, and I just want to keep it simple)
package com.net
{
import flash.net.URLLoader;
import flash.events.Event;
import com.events.CustomEvent;
public class CustomLoader extends URLLoader
{
// URLLoader already has a data property, so I used extraData
public var extraData:*;
override public function dispatchEvent(event: Event) : Boolean
{
var customEvent: CustomEvent = new CustomEvent(event.type, extraData, event.bubbles, event.cancelable);
return super.dispatchEvent(customEvent);
}
}
}
Now to use this with your CustomEvent class try this code in your .fla
import com.net.CustomLoader;
import com.events.CustomEvent;
var loader: CustomLoader = new CustomLoader();
loader.extraData = "Extra Data";
loader.load(new URLRequest("test.xml"));
loader.addEventListener(Event.COMPLETE, loadComplete);
function loadComplete(event: CustomEvent) : void
{
trace(event.data); // Extra Data
}
BAM! Custom data on your innately dispatched events!

The following shows the cleanest way to create a custom event. Typically event types have public static references typed in all capitol letters. When an event is dispatched, it passes an Event, or CustomEvent, object to the event handler method. This is where you can retrieve your passed value.
package com.hodgedev.events
{
import flash.events.Event;
public class CustomEvent extends Event
{
public static const VALUE_CHANGED:String = "VALUE_CHANGED";
public var value:Number;
public function CustomEvent(pValue:Number)
{
super(CustomEvent.VALUE_CHANGED);
value = pValue;
}
public override function clone():Event
{
return new CustomEvent(value);
}
}
}
When we dispatch events, we create a new instance of the event to be passed as such.
private var _someValue:int = 12;
dispatchEvent(new CustomEvent(_somevalue));

Related

Can't dispatch custom event from one class to another in Flash AS3

This is my custom event class:
package{
import flash.events.Event;
public class PetEvent extends Event{
public static const ON_CRASH:String = "onCrash";
public function PetEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false):void{
super(type, bubbles, cancelable);
}
override public function clone():Event {
return new PetEvent(type, bubbles, cancelable);
}
}
}
This is my game handler. I create a new instance of the class Surf from which I want to listen from.
package {
import flash.display.MovieClip;
import flash.events.Event;
public class GameHandler extends MovieClip {
public var newGame:Surf;
public function GameHandler() {
newGame = new Surf();
newGame.addEventListener(PetEvent.ON_CRASH, onCrash);
addChild(newGame);
}
public function onCrash(petEvent:PetEvent):void{
trace("MPAM");
var gameOver:GameOver = new GameOver(stage.stageWidth, stage.stageHeight);
addChild(gameOver);
newGame = null;
}
}
}
And the relevant lines from the Surf class:
public function startSurfing(timerEvent:TimerEvent):void
{
moveCatandDog();
for each ( var boat:Boat in armada)
{
boat.moveBoat(boatSpeed);
if ( cat.hitTestObject(boat) || dog.hitTestObject(boat) )
{
dispatchEvent( new PetEvent(PetEvent.ON_CRASH) );
gameTimer.stop();
}
}
}
So when Surf detects a crash I want it to send the event to GameHandler and GameHandler will create a GameOver instance.
I have tried everything and I don't even get a trace. I normally don't ask questions but this is for a uni project and I'm running out of time. I would really appreciate any feedback. Thanks!
Problem solved!
I had to change my Document Class to GameHandler and make a public static variable of stage.
Previously I had Surf as my Document Class because I had some keyboard listeners set to the stage.
So the PetEvent and the dispatch in Surf were correct. I changed the GameHandler as shown below, with another solution I found in StackOverflow.
Inside the constructor of GameHandler, if the stage is ready (not null) it sets it to the public static variable STAGE (via the init function), otherwise it adds a listener and when the stage is ready it does the same thing and removes the listener.
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.display.Stage;
public class GameHandler extends MovieClip {
public var newGame:Surf;
public static var STAGE:Stage;
public function GameHandler() {
if (stage){
init();
} else {
addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
}
newGame = new Surf();
newGame.addEventListener(PetEvent.ON_CRASH, onCrash);
addChild(newGame);
}
private function init(e:Event=null):void{
removeEventListener(Event.ADDED_TO_STAGE, init);
// store stage reference when stage ready
STAGE=stage;
}
public function onCrash(petEvent:PetEvent):void{
var gameOver:GameOver = new GameOver(stage.stageWidth, stage.stageHeight);
addChild(gameOver);
newGame = null;
}
}
}
and I imported GameHandler into Surf with:
import GameHandler;
so I can set the listeners in Surf to GameHandler.STAGE.addEventListener (...)
Thanks everyone for the suggestions!

Is it possible to listen the event triggered by another class?

I've got 3 classes:
The class that recognizes the change in URL (using SWFAddress by Asual):
package swfaddress {
import flash.display.MovieClip;
import com.asual.swfaddress.*;
public class SwfAddress extends MovieClip {
private var dispatcher:Dispatch = new Dispatch;
public function SwfAddress():void {
SWFAddress.addEventListener(SWFAddressEvent.CHANGE, onChange);
}
private function onChange(e:SWFAddressEvent):void {
dispatcher.changed();
}
}
}
The class "Dispatch" that validates the URL and dispatching Event when finished
package swfaddress {
import flash.events.Event;
import flash.events.EventDispatcher;
public class Dispatch extends EventDispatcher {
public static const CHANGED:String = "changed";
public function changed ():void {
// some operations validating the URL
dispatchEvent(new Event(Dispatch.CHANGED));
}
}
}
Other class in other package that should receive info when the validation process has finished.
package menu {
import swfaddress.*
public class MenuPanel extends MovieClip {
var swfRead:Dispatch = new Dispatch;
public function MenuPanel():void {
swfRead.addEventListener(Dispatch.CHANGED, onChange);
}
private function onChange(e:Event):void {
trace("Hello World");
}
}
And the "Hello World" never appeared in the output window - so I'm not sure if it's possible that my MenuPanel has a chance to receive an info about completing the validation triggered by some other class?
You are creating two different instances of the dispatcher, and so the instance that is being called to validate your URL is not the same instance that you are listening to in the MenuPanel class.
A simple way around this would be to make the Dispatch class a singleton, so that only one instance of it exists and you can reference it from different points in your application. This works by giving the Dispatch class a static method that returns a self-contained instance of itself. You then call Dispatch.getInstance() whenever you want a reference to the class instead of using the new keyword.
Dispatch:
package swfaddress
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class Dispatch extends EventDispatcher
{
// singleton instance
private static var _instance:Dispatch;
private static var _allowInstance:Boolean;
public static const CHANGED:String = "changed";
// get singleton instance of Dispatch
public static function getInstance():Dispatch
{
if (Dispatch._instance == null) {
Dispatch._allowInstance = true;
Dispatch._instance = new Dispatch();
Dispatch._allowInstance = false;
}
return Dispatch._instance;
}
public function Dispatch()
{
if (!Dispatch._allowInstance)
{
throw new Error("Error: Use Dispatch.getInstance() instead of the new keyword.");
}
}
public function changed():void {
//some operations validating the URL
dispatchEvent(new Event(Dispatch.CHANGED));
}
}
}
Getting a reference:
private var dispatcher:Dispatch = Dispatch.getInstance();

Custom Events with Singleton EventDispatcher, using getDefinition from child

EDIT: For whatever reason, it works in the browser but not when compiled/debugged within the IDE.
I can't get my external SWFs to pick up on dispatches from my singleton event manager (EventDispatcher). Here are the particulars:
I add children from an external SWF using the getDefinition method to my main SWF.
I'm using a singleton EventDispatcher that is in charge of listeners and dispatching.
Using a custom event class.
In this code, I am trying to get a mute button to tell the main SWF that the mute icon has been clicked (SoundClipEvent.MUTE_CLICK). After the sound has been muted, it should dispatch the event (SoundClipEvent.STATE) and confirm to the muteIcon the state. Currently, the mute icon successfully dispatches the MUTE_CLICK event and the main SWF document class is able to pick it up. MuteIcon (child SWF MC) hears nothing from the singleton.
Your help in this problem is greatly appreciated!
SoundClipManager.as:
import flash.events.Event;
import flash.events.EventDispatcher;
public dynamic class SoundClipManager extends EventDispatcher {
private static var isMuted:Boolean;
public function SoundClipManager(blocker:SingletonBlocker):void {
super();
//
if (blocker == null) {
throw new Error("Error: Instantiation failed; Use SoundClipManager.getInstance()");
}
}
public static function get muted():Boolean {
return SoundClipManager.isMuted;
}
public static function set muted(value:Boolean) {
SoundClipManager.isMuted = value;
//
SoundClipManager.getInstance().dispatchEvent(new SoundClipEvent(SoundClipEvent.STATE,SoundClipManager.muted));
}
public static function getInstance():SoundClipManager {
if (instance == null) {
instance = new SoundClipManager(new SingletonBlocker());
}
return instance;
}
public override function dispatchEvent(evt:Event):Boolean {
return super.dispatchEvent(evt);
}
private static function stateChanged(evt:*) {
trace('state changed!');
}
}
internal class SingletonBlocker {}
MuteIcon.as
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
//
public dynamic class IconMute extends MovieClip {
public function IconMute() {
this.addEventListener(Event.ADDED_TO_STAGE,this.addedToStage);
//
SoundClipManager.getInstance().addEventListener(SoundClipEvent.STATE,this.soundClipManagerStateChanged);
}
//
// Methods, Private
//
//
// Events
//
private function muteClick(evt:MouseEvent) {
SoundClipManager.getInstance().dispatchEvent(new SoundClipEvent(SoundClipEvent.MUTE_CLICK));
}
//
private function addedToStage(evt:Event) {
this.addEventListener(MouseEvent.CLICK,this.muteClick);
}
//
private function soundClipManagerStateChanged(evt:*) {
trace("state changed!");
}
}
SoundClipEvent.as
package {
//
import flash.events.Event;
//
public class SoundClipEvent extends Event {
public static const MUTE_CLICK:String = "muteClick";
public static const STATE:String = "state";
//
public var muted:Boolean;
public function SoundClipEvent(type:String,muted:Boolean = false) {
if(muted) this.muted = muted;
//
super(type,true,false);
}
}
}
getDefinitionByName(className) method will work only if className was mensioned somewhere in your code. You may just import className class in a file where you're going to call getDefinitionByName(className). That should help!

Custom event dispatchment location

I've been looking into custom event (listeners) for quite some time, but never succeeded in making one. There are so many different methods, extending the Event class, but also Extending the EventDispatcher class, very confusing!
I want to settle with this once and for all and learn the appropriate technique.
package{
import flash.events.Event;
public class CustomEvent extends Event{
public static const TEST:String = 'test'; //what exac is the purpose of the value in the string?
public var data:Object;
public function CustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, data:Object = null):void
{
this.data = data;
super();
}
}
}
As far as I know a custom class where you set the requirements for the event to be dispatched has to be made:
package
{
import flash.display.MovieClip;
public class TestClass extends MovieClip
{
public function TestClass():void {
if (ConditionForHoldToComplete == true) {
dispatchEvent(new Event(CustomEvent.TEST));
}
}
}
}
I'm not sure if this is correct, but it should be something along the lines of this.
Now What I want is something like a mouseevent, which can be applied to a target and does not require a specific class.
It would have to work something like this:
package com.op_pad._events{
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.EventDispatcher;
import flash.events.Event;
public class HoldEvent extends Event
{
public static const HOLD_COMPLETE:String = "hold completed";
var timer:Timer;
public function SpriteEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super( type, bubbles, cancelable );
timer = new Timer(1000, 1);
//somehow find the target where is event is placed upon -> target.addEventlistener
target.addEventListener(MouseEvent.MOUSE_DOWN, startTimer);
target.addEventListener(MouseEvent.MOUSE_UP, stopTimer);
}
public override function clone():Event
{
return new SpriteEvent(type, bubbles, cancelable);
}
public override function toString():String
{
return formatToString("MovieEvent", "type", "bubbles", "cancelable", "eventPhase");
}
//////////////////////////////////
///// c o n d i t i o n s /////
//////////////////////////////////
private function startTimer(e:MouseEvent):void
{
timer.start();
timer.addEventListener(TimerEvent.TIMER_COMPLETE, complete);
}
private function stopTimer(e:MouseEvent):void
{
timer.stop()
}
public function complete(e:TimerEvent):void {
dispatchEvent(new HoldEvent(HoldEvent.HOLD_COMPLETE));
}
}
}
This obviously won't work, but should give you an idea of what I want to achieve. This should be possible because mouseevent can be applied to about everything.The main problem is that I don't know where I should set the requirements for the event to be executed to be able to apply it to movieclips and sprites.
You are almost there actually, just for the last part, wouldn't this be more of an OOP related issue than stricly a confusion about the way of using custom events ?
Usually, Events in AS3 are value objects whose sole responsibility is to transport information from the event dispatcher to the listener(s). The dispatcher dispatches the event each time a defined momentum is reached, and the listener(s) may or may not react when this happens.
In the example above, I guess it is up to the listener to start a timer and so on when a mouse-down has been detected. In a more sophisticated context the Event could independently trigger more than one listeners actioning separate tasks which neither the Dispatcher nor the Event itself should have to bother about, that is probably why it's worth avoiding amending the dispatcher or the event itself with any soever logic.
For your very example, you could maybe create a handler checking if the mouse has been held down?
The following is just pseudocode, and there are obviously tons of other ways to get to the same result:
public class MouseDownHandler
{
// ...
public function( target:Sprite ) {
this.target = target;
start();
}
public function start():void{
// Listen for the target's mouseUp event
}
public function dispose():void{
// Stop listeners and eventually the timer
}
private function onMouseDown(e:MouseEvent):void{
// Start timer + listening for the stage's mouse up event (target.stage)
}
private function onMouseUp(e:Event):void{
// Cancel timer
}
private function onTimerComplete(e:TimerEvent):void {
dispatchEvent(new HoldEvent(HoldEvent.HOLD_COMPLETE));
}
}
Which could be reused for example this way:
var mc:MovieClip = new MovieClip(); ...
var mouseHandler:MouseDownHandler = new MouseDownHandler(mc);
mouseHandler.addEventListener(HoldEvent.HOLD_COMPLETE, onMcHoldComplete);
... or this way :
public class TestMovieClip extends MovieClip
{
private var mouseHandler:MouseDownHandler;
public function TestMovieClip() {
mouseHandler = new MouseDownHandler(this);
mouseHandler.addEventListener(HoldEvent.HOLD_COMPLETE, onMouseHoldComplete);
}
private function onMouseHoldComplete(e:HoldEvent):void {
// Do something
}
}
I just use robber penners signals. Very easy to use.
http://github.com/robertpenner/as3-signals

Override Clone() in custom event for AS3... need help

I have created a custom even class which is pretty basic. But when calling an event and then relaying that event to another class I have encountered the "cannot transform thisEvent into thisOtherEvent" error.
I realize this is because I needed to override the Clone function in my custom event like so:
package com
{
import flash.disply.*;
import flash.events.Event;
public class MyCustomEvents extends Event
{
public static const SOME_EVENT:String = "some_event";
public var info:Object;
public function MyCustomEvents($type:String, $info:Object,$bubbles:Boolean = false, $cancelable:Boolean = false)
{
super($type, $bubbles, $cancelable);
this.info = $info;
}
public override function clone():Event {
return new MyCustomEvents($type, $bubbles, $cancelable);
}
}
}
However I am still getting this error when I dispatch the event. Anything else I might be missing?
here is the error:
TypeError: Error #1034: Type Coercion failed: cannot convert com.greensock.events::TransformEvent#d8df709 to com.customEvents.MyCustomEvents.
I tried casting the event in the code like so:
var deleteImgEvent:MyCustomEvent = new MyCustomEvent(MyCustomEvents.IMAGE_DELETE, {imgData: getImg}, true, false); this.dispatchEvent(deleteImgEvent as MyCustomEvents);
Still no luck.
UPDATE:
Ok, seems like the problem is in the greensock Transform library. When the event handler for my custom event is called, I run a function of the TransformManager class.
_manager.deleteSelection();
Inside that class it dispatched a TransformEvent. Not sure why, but it is reading that delete event as a MyCustomEvent.
/**
* #usage
* var myEvent:CustomEvent = new CustomEvent(CustomEvent.EVENT_TYPE_A, { integerRelatedToEvent: 5, stringRelatedToEvent: 'easy' });
* addEventListener(CustomEvent.EVENT_TYPE_A, traceCustomEvent);
* dispatch(myEvent);
* function traceCustomEvent ($e:CustomEvent):void {
* trace($e.type);
* }
*/
package {
import flash.events.Event;
public class CustomEvent extends Event {
// Types:
public static const EVENT_TYPE_A:String = 'CustomEvent.EVENT_TYPE_A';
public static const EVENT_TYPE_B:String = 'CustomEvent.EVENT_TYPE_B';
// Components:
private var _customDatum:Object;
public function get customDatum ():Object { return _customDatum; }
public function CustomEvent ($type:String, $customDatum:Object) {
super($type);
_customDatum = $customDatum;
}
public override function clone ():Event {
return new CustomEvent(type, _customDatum);
}
}
}
"When creating your own custom Event
class, you must override the inherited
Event.clone() method in order for it
to duplicate the properties of your
custom class. If you do not set all
the properties that you add in your
event subclass, those properties will
not have the correct values when
listeners handle the redispatched
event."
package com.events;
{
import flash.events.Event;
public class XMLLoaderEvent extends Event
{
public static const XML_LOADED:String = "XML_Loaded";
public var data:*;
public var properties:Object;
public function XMLLoaderEvent( type:String,_data:*,bubbles:Boolean = false,cancelable:Boolean = false):void
{
super( type, bubbles, cancelable );
data = _data;
}
// Override clone
override public function clone():Event
{
return new XMLLoaderEvent( type, data, bubbles, cancelable);
}
}
}
Don't know if that's it but you have an extra parameter $info:Object into your custom event, but you don't pass it in your clone contructor.
return new MyCustomEvents(type, info, bubbles, cancelable);
I think you need the clone function to return a MyCustomEvents type. Not an Event type. And you need to add the info parameter as stated by the previous poster.
package com {
import flash.display.*;
import flash.events.Event;
public class MyCustomEvents extends Event {
public static const SOME_EVENT:String = "some_event";
public var info:Object;
public function MyCustomEvents($type:String, $info:Object,$bubbles:Boolean = false, $cancelable:Boolean = false) {
super($type, $bubbles, $cancelable);
this.info = $info;
}
public override function clone():MyCustomEvents {
return new MyCustomEvents(this.type, this.info, this.bubbles, this.cancelable);
}
}
}