Is there any simple -- or suggested -- way in order to send a parameter along with a custom event? Or even just a way to pass variables between two classes?
My program is a sort of simple Civ/Age of Empires kind of game, where you can place buildings on tiles. This would work as follows:
Player clicks on the icon on the HUD, which creates an dispatches an event which is received by the PLAYER class.
The PLAYER class changes a value depending on which building is held (clicked on).
Player clicks on the tile in the grid to place it, which dispatches an event which is received by the PLAYER class.
The PLAYER class creates a building object and adds it to an array within the PLAYER class.
An example of how I'd want the code to work:
icon.as
private function onMouseClick(e:MouseEvent = null):void {
var iconClickedEvent:Event = new Event("BUILDING_HELD", buildingType); // passes "buildingType" through the event somehow
stage.dispatchEvent(iconClickedEvent);
}
tile.as
private function onMouseClick(e:MouseEvent = null):void {
var buildingPlacedEvent:Event = new Event("BUILDING_PLACED", xRef, yRef);// passes "xRef" & "yRef", the tile's co-ordinate
stage.dispatchEvent(buildingPlacedEvent);
}
player.as
private function init(e:Event):void {
stage.addEventListener("BUILDING_HELD", buildingHeld(buildingType));
stage.addEventListener("BUILDING_PLACED", placeBuilding(xRef, yRef));
}
private function buildingHeld(building:int):void {
buildingType = building;
}
private function placeBuilding(xRef:int, yRef:int):void {
switch(buildingType){
case 1: // main base
MainBaseArray.push();
MainBaseArray[length-1] = new MainBase(xPos, yPos); // create new object with the references passed
break;
}
}
The best way to manage this is to create custom event classes for each of your events (or event types).
If you create a class that inherit Event, it will be usable in the same ways that a standard Event, but can contain custom values or methods.
Here's an example of such class :
public class BuildingEvent extends Event {
// contains an event name. Usefull to ensure at compile-time that there is no mistape in the event name.
public static const BUILDING_HELD:String = "BUILDING_HELD";
private var _buildingType:int;
// the constructor, note the new parameter "buildingType". "bubbles" and "cancelable" are standard parameters for Event, so I kept them.
public function BuildingEvent(type:String, buildingType:int, bubbles:Boolean = false, cancelable:Boolean = false) {
super(type, bubbles, cancelable);
_buildingType = buildingType;
}
// using a getter ensure that a listening method cannot edit the value of buildingType.
public function get buildingType() {
return _buildingType;
}
}
We can then use this class like this :
// to dispatch the event
private function onMouseClick(e:MouseEvent = null):void {
var iconClickedEvent:BuildingEvent = new BuildingEvent(BuildingEvent.BUILDING_HELD, buildingType);
stage.dispatchEvent(iconClickedEvent);
}
// to listen to the event
private function init(e:Event):void {
stage.addEventListener(BuildingEvent.BUILDING_HELD, buildingHeld);
}
private function buildingHeld(event:BuildingEvent):void {
buildingType = event.buildingType;
}
Related
I'm building a game. When you die or win a level you're prompted to continue or return to the main menu. If you chose to go to the main menu you can start a new game. When you start a new game, the game is object is created again & it's children have lost their reference to the stage. I'm not sure why this is happening and I've spent over a week trying to figure out why. Here's some code (and descriptions of the code) from the game that should hopefully provide enough insight as to why the problem may be occurring:
if the "new game" button is clicked on the startMenu the NavigationEvent.START event is dispatched. a LevelEvent.COMPLETE event is dispatched by WeeBeeGame when the level is completed.
public class DocumentClass extends MovieClip {
public var startMenu:StartMenuGenerator = new StartMenuGenerator();
public var weeBeeGame:WeeBeeGame;
public var youWonBox:YouWonBox = new YouWonBox();
public function DocumentClass() {
// constructor code
addChild(startMenu);
startMenu.addEventListener(NavigationEvent.START, startGameHandler);
}
public function startGameHandler(e:NavigationEvent) : void {
this.removeChild(startMenuBG);
removeChild(startMenu);
weeBeeGame = new WeeBeeGame();
this.addChild(weeBeeGame);
weeBeeGame.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler);
}
public function levelCompleteHandler(e:LevelEvent) : void {
youWonBox.x = this.stage.stageWidth/2;
youWonBox.y = this.stage.stageHeight/2;
addChild(youWonBox);
youWonBox.addEventListener(MouseEvent.CLICK, mouseClickHandler);
}
private function mouseClickHandler(e:MouseEvent) : void {
if(e.target.name === "mainmenubtn"){
mainmenuHandler();
}
}
private function continueHandler() : void {
youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
private function mainmenuHandler() : void {
youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
WeeBeeGame.collisionDOC.removeChildren();
removeChild(weeBeeGame);
weeBeeGame = null;
this.addChild(startMenuBG);
addChild(startMenu);
removeChild(youWonBox);
}
}
the code that dispatches a LevelEvent.COMPLETE event is not shown but it dispatches when the level is complete. collisionDOC needs to be static because it's needed in a lot of other classes and holds the display objects needed for a 3rd party pixel-level collision detection.
public class WeeBeeGame extends MovieClip {
public var bee: Bee;
public var beeHurt:BeeHurt;
public var spawningDaemon:SpawningDaemon;
public static var collisionDOC:DisplayObjectContainer;
public function WeeBeeGame() {
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
}
private function addedToStageHandler(e:Event) : void {
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
collisionDOC = new MovieClip();
addChild(collisionDOC);
bee = new Bee();
collisionDOC.addChild(bee);
beeHurt = new BeeHurt(bee.x, bee.y);
addChild(beeHurt);
beeHurt.visible = false;
spawningDaemon = new SpawningDaemon(currentLevel);
this.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler, false, 0, true);
}
private function levelCompleteHandler(e:LevelEvent) : void {
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
}
the first line to throw a 1009 error (Cannot access a property or method of a null object reference) is the line containing stage.mouseX because the stage reference is null.
public class Bee extends MovieClip {
public function Bee() {
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(e:Event) : void {
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
this.x = this.stage.stageWidth / 2;
this.y = this.stage.stageHeight - this.stage.stageHeight / 9;
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
}
private function enterFrameHandler(e:Event) : void {
if(stage == null){
trace('stage is null' + stage.mouseX);
}
}
}
When the swf is first opened and a new WeeBeeGame is created its children have references to the stage. But when WeeBeeGame and it's children are removed from the display list and memory they lose reference and even if they're re-instantiated their references are still lost. How do I fix this? I'm very confused. Thank you all!!
ENTER_FRAME handlers continue to execute even when the display object is removed form the stage. So when you removeChild(weeBeeGame) those ENTER_FRAME handlers are still trying to access stage.mouseX each frame. You need to stop the ENTER_FRAME handlers.
Probably the easiest fix is to add an Event.REMOVED_FROM_STAGE handler to remove the Event.ENTER_FRAME handlers.
A better fix IMO is to not add any ENTER_FRAME handlers from your game objects, but rather expose a public function like update() which gets called from a single ENTER_FRAME handler in your WeeBeeGame when the game is running. Then to stop the game completely you can stop all updates by simply removing that single ENTER_FRAME handler. This is a common practice.
lots of help from you guys :). My next question is here :).
I have timer in class MyTimer.as and Thief1_mc.as movie clip.
How can I addChild(Thief1_mc) on the stage from MyTimer? Everything looks simple, the only problem is "stage" property. MyTimer class cannot send "stage" as an argument because is not on the stage itself. I tried adding MyTimer on the stage in Main class like addChild (MyTimer), the trace says MyTimer is on the stage but I still cannot pass the stage argument to the Thief1_mc. I need this argument to be sent because the class Thief1_mc has to add itself on the stage using the property "stage".
The code:
public class Thief1_mc extends MovieClip
{
//this variable type Stage will contain stage
private var stageHolder:Stage;
public function Thief1_mc()
{
//constructor
}
//function that creates this object with passed "stage" argument from the caller
public function createItself(st):void
{
//variable that contain the stage so I can use this argument anywhere in the class
stageHolder = st;
//i have to refer to the stage by passed "st" parameter to create this object
stageHolder.addChild(this);
//initial position
this.x = 380;
this.y = 230;
}
}
}
MyTimer class and "_thief1.createItself(stage)" caller with stage arument
public class MyTimer extends Sprite
{
private static var nCount:Number = 120;
private static var currentCount:Number;
private static var _timer:Timer = new Timer(1000,nCount);
private static var _timerDispather:Timer;
private static var _thief1:Thief1_mc = new Thief1_mc ;
public function MyTimer()
{
// constructor code
}
//another timer
private static function increaseInterval(interval:int):void
{
_timerDispather = new Timer(interval);
_timerDispather.addEventListener(TimerEvent.TIMER, onUpdateTimeAnotherTimer);
_timerDispather.start();
}
//another timer;
private static function onUpdateTimeAnotherTimer(e:Event):void
{
_thief1.createItself(stage);//the most important part
}
public static function activateTimer():void
{
currentCount = nCount;
_timer.addEventListener(TimerEvent.TIMER, onUpdateTime);
_timer.start();
}
public static function deactivateTimer():void
{
_timer.removeEventListener(TimerEvent.TIMER, onUpdateTime);
_timer.stop();
_timer.reset();
currentCount = nCount;
//another timer
_timerDispather.removeEventListener(TimerEvent.TIMER, onUpdateTimeAnotherTimer);
_timerDispather.stop();
_timerDispather.reset();
}
private static function onUpdateTime(e:Event):void
{
currentCount--;
if (currentCount == 0)
{
_timer.removeEventListener(TimerEvent.TIMER, onUpdateTime);
_timer.stop();
_timer.reset();
}
}
}
}
Your code is backwards in a few places. It does not flow very nicely, and the issues you having now are going to be tenfold at some stage in your project.
Firstly, your MyTimer class should not be extending Sprite. It does not get rendered and does not represent anything graphically.
Secondly, your timer class is taking on more than it should. I would revise it to manage your timers and timer events only. Create a list within your timer class that will contain some other elements which can have a method triggers to do other stuff, like creating and adding Thief1_mc.
A simplified version of this might look like:
public class Updater
{
private var _timer:Timer;
private var _toUpdate:Vector.<IUpdatable> = new Vector.<IUpdatable>();
public function Updater()
{
_timer = new Timer(60);
_timer.start();
_timer.addEventListener(TimerEvent.TIMER, _notifyUpdatables);
}
private function _notifyUpdatables(e:TimerEvent):void
{
for each(var i:IUpdatable in _toUpdate)
{
i.update(this);
}
}
public function addUpdatable(updatable:IUpdatable):void
{
_toUpdate.push(updatable);
}
public function removeUpdatable(updatable:IUpdatable):void
{
var index:int = _toUpdate.indexOf(updatable);
if(index >= 0) _toUpdate.splice(index, 1);
}
}
From here we need to create an interface which we will implement on classes that we want to be able to call update() on each time the Updater timer ticks:
public interface IUpdatable
{
function update(updater:Updater):void;
}
Now what I would do in your case is have a class that does extend Sprite and manages the graphics of the application / game. It will implement the IUpdatable interface like I have described and also could deal with adding your Thief1_mc:
public class View extends Sprite implements IUpdatable
{
public function update(updater:Updater):void
{
// Create a Thief.
var thief:Thief = new Thief();
updater.addUpdatable(thief);
addChild(thief);
}
}
Your Thief can take advantage of the IUpdatable interface we have and be added to the update queue when it is created, as I've done above. Just to have a complete example, here's the Thief class:
public class Thief extends Sprite implements IUpdatable
{
public function update(updater:Updater):void
{
// Make this Thief so some stuff.
//
}
}
And here's how you can tie it all together in your document class:
public class App extends Sprite
{
private var _updater:Updater;
private var _view:View;
public function App()
{
_updater = new Updater();
_view = new View();
_updater.addUpdatable(_view);
stage.addChild(_view);
}
}
This might be a bit overwhelming at first, and seem like a lot of work, but you now have a nice clean foundation to add more elements easily.
Rather than having your one class trying to manage timers and add Thieves like you had initially, we've separated the responsibilities and tightened up the flow a little. The Updater deals purely with storing IUpdatable instances and calling their update() method each time the Timer within it ticks. The View class manages the graphics and will also add a Thief each time it is updated via the Updater. The View was added to the stage initially, so all you need to do is add the thieves into itself to have them show up.
If you take this and restructure how the timers work within Updater, I think you'll be where you wanted but with a significantly better understanding and structure.
Can any one give me a simple example on how to dispatch an event in actionscript3 with an object attached to it, like
dispatchEvent( new Event(GOT_RESULT,result));
Here result is an object that I want to pass along with the event.
In case you want to pass an object through an event you should create a custom event. The code should be something like this.
public class MyEvent extends Event
{
public static const GOT_RESULT:String = "gotResult";
// this is the object you want to pass through your event.
public var result:Object;
public function MyEvent(type:String, result:Object, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.result = result;
}
// always create a clone() method for events in case you want to redispatch them.
public override function clone():Event
{
return new MyEvent(type, result, bubbles, cancelable);
}
}
Then you can use the code above like this:
dispatchEvent(new MyEvent(MyEvent.GOT_RESULT, result));
And you listen for this event where necessary.
addEventListener(MyEvent.GOT_RESULT, myEventHandler);
// more code to follow here...
protected function myEventHandler(event:MyEvent):void
{
var myResult:Object = event.result; // this is how you use the event's property.
}
This post is a little old but if it can help someone, you can use DataEvent class like so:
dispatchEvent(new DataEvent(YOUR_EVENT_ID, true, false, data));
Documentation
If designed properly you shouldn't have to pass an object to the event.
Instead you should make a public var on the dispatching class.
public var myObject:Object;
// before you dispatch the event assign the object to your class var
myObject = ....// whatever it is your want to pass
// When you dispatch an event you can do it with already created events or like Tomislav wrote and create a custom class.
// in the call back just use currentTarget
public function myCallBackFunction(event:Event):void{
// typecast the event target object
var myClass:myClassThatDispatchedtheEvent = event.currentTarget as myClassThatDispatchedtheEvent
trace( myClass.myObject )// the object or var you want from the dispatching class.
I have a big mobile application in flex 4.5, and using "big" I mean big..
50+ views
50+ custom components
10+ custom events
50+ custom classes (not UIComponents)
Lot of skins, etc...
I never removed an EventListener, and now I'm looking at the flash profiler and I there are a lot of memory leaks..
I actually thought that using navigator.pushView or navigator.popView() or navigator.popToFirstView() will remove any reference of all the object/variable in the view itself and garbaged collected..
I'm trying to fix my code but I have a lot of problems understanding how to works with event listeners...
I think that it will be easier if I use some examples instead of describing every possible case...
Example 1:
private function XXX():void
{
var x:ClassA = new ClassA();
x.addEventListener(CustomEvent.GETA, doSomething);
x.addEventListener(CustomEvent2.TESTB, doSomethingElse);
}
private function doSomething(e:CustomEvent):void{}
private function doSomethingElse(e:CustomEvent2):void{}
Do I need to remove both eventListener after one is fired?
if yes, do I need to remove both event listener both in doSomething and in doSomethingElse?
Is it better to use a weakReference in the addeventlistener?
x.addEventListener(CustomEvent.GETA, doSomething, false, 0, true);
Example 2:
tile is a TileGroup, but it could be everything...
protected function activate(event:ViewNavigatorEvent):void
{
tile.removeAllElements();
for (var i:int = 1 ; i < functionCount.length ; i++)
{
var t:ImageButton = new ImageButton();
t.label = "";
switch (functionCount.getItemAt(i))
{
case "Val1":
t.addEventListener(MouseEvent.CLICK,function():void{goToAgenda();});
break;
case "Val2":
t.addEventListener(MouseEvent.CLICK,function():void{goToAdmin();});
break;
case "Val3":
t.addEventListener(MouseEvent.CLICK,function():void{goToMyStore();});
break;
case "Val4":
t.addEventListener(MouseEvent.CLICK,function():void{openBI();});
break;
}
tile.addElement(t);
}
The click on the ImageButton call a Function that does somethings and than call the navigator.pushView
If I change the view, should I remove every EventListener from every button?
Where is the best place to do that?
If I have a view with 15000 compoenents, do I have to manually remove all the eventlistener added to each one when the view is removed???
EDIT
I mainly need to remove all event listener when a view is removed from stage so instead of doing it manually every time I thought an alternative and I create a simple class...
package utils
{
import mx.collections.ArrayCollection;
public class Evt
{
public static var listaEvt:ArrayCollection = new ArrayCollection();
private var object:* = null;
private var event:* = null;
private var functio:* = null
public function Evt(obj:*, evt:*, funct:Function)
{
this.object = obj;
this.event = evt;
this.functio = funct;
}
public static function addEvt(obj:*, evt:*, funct:Function):void
{
var t:Evt = new Evt(obj, evt, funct);
listaEvt.addItem(t);
}
public static function clear():void
{
var tmpArr:ArrayCollection = new ArrayCollection();
tmpArr.addAll(listaEvt);
listaEvt = new ArrayCollection();
for (var i:int = 0 ; i < tmpArr.length ; i++)
{
var t:Evt = tmpArr.getItemAt(i) as Evt;
if (t.object != null && t.event != null && t.functio != null)
{
if (t.object.hasEventListener(t.event))
t.object.removeEventListener(t.event, t.functio);
}
}
}
}
}
In my code anytime I do not need weak reference I call (also using anonymous function):
// t can be any component with a event associated...
var f:Function = function():void{navigator.pushView(FotoGallery,data);};
Evt.addEvt(t, MouseEvent.CLICK, f);
t.addEventListener(MouseEvent.CLICK,f);
Then when I enter a new view I do a simple Evt.clear();
Is this approach effective? any advise to improve/change it?
Ultimately larger applications may benefit from frameworks such as Swiz Framework or Pure MVC, which can assist in:
Inversion of Control / Dependency Injection
Event handing and mediation
A simple life cycle for asynchronous remote methods
A framework that is decoupled from your application code
Swiz, for example, can use meta data tags to mediate events either catching events from the display list or injecting a dispatcher for events originating from non-UI sources.
[Dispatcher]
public var dispatcher:IEventDispatcher;
[EventHandler( event="UserEvent.ADD_USER" )]
public function handleAddUserEvent( event:UserEvent ):void
{
// do stuff
}
Otherwise, as mentioned, weak references in event listeners is an option:
addEventListener(Event.COMPLETE, completeHandler, false, 0, true);
Views requiring isolated event handling can adapt to the intrinsic lifecycle of added and removed from stage. Add listeners on addedToStage when the display object is ready to handle signals from the display list, and dispose listeners on removedFromStage when the display object no longer requires event handling.
package
{
import flash.display.Sprite;
import flash.events.Event;
public class MyClass extends Sprite
{
public function MyClass()
{
super();
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
protected function addedToStageHandler(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
/* add event listeners here */
}
protected function removedFromStageHandler(event:Event):void
{
removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
/* remove event listeners here */
}
protected function dispose():void
{
/* any remaining cleanup */
}
}
}
I created a custom event class, and a EventDispatcher-derived class.
What i did, is putting some logic in my EventDispatcher class, and then dispatched the custom event, the problem is i cant manage to listen to the event from my Main app.
I have an Arraycollection which i create and edit in the main app, than i want to send it to the EventDispatcher , and make a few checks, and than i want to dispatch my ArrayCollection back to main app for Binding with Visual objects.
If my entire code logic is incorrect, please tell me [=
Here is some code
The EventDispatcher sub-class
public class LoadData extends EventDispatcher
{
public var sendData:DataSender = new DataSender('DataLoader',dataList,true);
private var dataList:ArrayCollection = new ArrayCollection();
dispatchEvent(sendData);
}
The custom Event class
public class DataSender extends Event {
public var data:ArrayCollection = new ArrayCollection;
public function DataSender(type:String, data:ArrayCollection, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.data = data;
}
}
The Main app
var DataEvent:LoadData = new LoadData(dataList);
addEventListener('DataLoader',datacapture);
public function datacapture(event:DataSender):void{
check.dataProvider = event.data;
}
You would have to make sure that the event is not dispatched before you actually listen to it. According to your code, it's not very clear when the event is dispatched.
Without going into the details of your logic, you could probably do something like this, please note that I'm not recommending this implementation, simply illustrating my answer!
//In the LoadData Class
public var sendData:DataSender = new DataSender('DataLoader',dataList,true);
private var dataList:ArrayCollection = new ArrayCollection();
public function dispatch():void
{
dispatchEvent(sendData);
}
//In your Main App
var dataEvent:LoadData = new LoadData(dataList);
dataEvent.addEventListener('DataLoader',datacapture);
dataEvent.dispatch();
Yes, your code is royally messed up. First of all, I suggest you read up on Object Oriented Programming.
Then take a look at this:
public class LoadData extends EventDispatcher {
public var sendData:DataSender;
private var dataList:ArrayCollection;
public function LoadData() {
//This is the CONSTRUCTOR which is run when you do new LoadData()
}
//This function is called when you want to dispatch the event
private function dispatch() {
dataList = new ArrayCollection();
sendData = new DataSender('DataLoader',dataList,true);
dispatchEvent(sendData);
}
}
Then your main app is like this:
var dataEvent:LoadData = new LoadData(dataList);
dataEvent.addEventListener('DataLoader', datacapture);
Then, whenever the dataEvent.dispatch() function is called, your event handler will be invoked.