Custom events not working in AS3 - actionscript-3

I have an event called SelectEvent. Whenever I click the buyer, the select event should be launched. But that is not what is happening.
My code of base:
package
{
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
public class FashionFrenzy extends MovieClip
{
public var Buyer_mc:Buyer;
public var Buyers:Array;
private var BuyerNumber:Number;
public var xpositions:Array;
public var ypositions:Array;
public var SelectedBuyer:Number;
public function FashionFrenzy()
{
GameTimeController();
xpositions=new Array();
xpositions.push(523,563,603);
ypositions=new Array();
ypositions.push(377,377,377);
Buyers = new Array ;
BuyerNumber=0;
Time_mc.gameTimer.addEventListener(TimerEvent.TIMER,GenerateBuyers);
addEventListener(SelectEvent.BUYERSELECT,showselectbuyer);
}
public function GameTimeController()
{
Time_mc.StartGame();
}
public function showselectbuyer(event:SelectEvent):void
{
trace("Bamba");
}
public function GenerateBuyers(event:TimerEvent):void
{
if(BuyerNumber<6)
{
if (Math.random() < 0.01)
{
var position:Number = Math.floor(Math.random()*3);
var newBuyer_mc = new Buyer(xpositions[position],ypositions[position],BuyerNumber);
ypositions[position]-=40;
Buyers.push(newBuyer_mc);
addChild(newBuyer_mc);
BuyerNumber++;
}
}
for each (var buyer_mc:Buyer in Buyers)
{
if(buyer_mc.walking==true)
{
buyer_mc.Enter();
}
}
}
}
}
My code of the buyer:
package
{
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
public class Buyer extends MovieClip
{
public var buyerTimer:Timer;
public var walking:Boolean;
public var stopposition:Number;
public var buyerCode:Number;
public function Buyer(startx:Number, stopy:Number, code:Number)
{
x=startx;
stopposition=stopy;
walking=true;
buyerCode=code;
BuyerProperties();
}
public function Enter():void
{
if(y>stopposition)
{
walking=false;
StartFunction();
}
else
{
y= y+3;
}
}
public function BuyerProperties():void
{
buyerTimer = new Timer( 25 );
trace(" I am generated");
}
public function StartFunction():void
{
buyerTimer.start();
addEventListener(MouseEvent.CLICK,Select);
trace("My timer starts now within Buyer.as");
}
public function Select(event:MouseEvent):void
{
trace(buyerCode);
dispatchEvent( new SelectEvent(SelectEvent.BUYERSELECT));
}
}
}
Now that when I'm clicking the buyer, the MouseEvent.CLICK is activated and then in the buyer.as, buyercode is traced on the screen. But the event is not dispatched or else it is dispatched but the eventlistener is not executing the code. I'm not getting any runtime errors. the function "showselectbuyer" is never even launched.
How should I solve it?

The accepted answer is incorrect. The provided solution works but for the wrong reasons. It creates a direct dependency to the object supposed to listen to the custom event and then makes that object be the dispatcher. All together this makes the whole idea of using event/custom event unnecessary at best since in that case a simple callback would work just as well. Instead using the useCapture flag would make the whole system work as expected:
addEventListener(SelectEvent.BUYERSELECT,showselectbuyer, true);
The accepted solution is also the inverse way of dealing with non DisplayObject event propagation. The dispatcher should be the listener (no dependencies) and not the listener should be the dispatcher (dependency necessary).

The trouble with custom events is that they don't bubble up the display list, so in case your event to be registered you need to dispatch it to the class instance that has a listener to that event attached. In your case it's an instance of FashionFrenzy. Apparently buyers don't know about a fashion frenzy instance they are running in, so they dispatch the event to themselves and wonder why no one else listens. In order to resolve this, either attach the listener to the buyer, or in the buyer class dispatch the event to parent, which is apparently the instance you want to receive the event.

Related

Access of undefined property issues in AS3

I am having a bit of trouble with some AS3. First time using this language and have more experience with web development then OOP so am getting a bit confused.
I am trying to make it so that when someone clicks a 'powerbutton' which is a "movieclip" symbol within flash then another symbol should then become visible. This is all being done within the Kitchen class.
The code for the main class is which i got from a youtube tutorial video i followed;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
public function DragFood() {
myKitchen = new Kitchen;
originalPosition = new Point (x, y);
buttonMode = true;
addEventListener (MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent):void
{
parent.addChild(this);
startDrag();
stage.addEventListener (MouseEvent.MOUSE_UP, stageUp);
}
protected function stageUp (event:MouseEvent):void
{
stage.removeEventListener (MouseEvent.MOUSE_UP, stageUp);
stopDrag();
if (dropTarget)
{
if(dropTarget.parent.name == "bowl")
{
trace("The " + this.name + " is in the bowl");
this.visible = false;
} else {
returnToOriginalPosition();
}
} else {
returnToOriginalPosition();
}
}
protected function returnToOriginalPosition():void
{
x = originalPosition.x;
y = originalPosition.y;
}
}
}
Within it i call the other class;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
The code for the kitchen class is;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Kitchen extends MovieClip
{
// This is a function. This particular function has the same name as our class and therefore will be executed first
public function Kitchen()
{
// This is a "call" to another function that is defined later in the program.
init();
trace("Hello world");
}
public function init():void
{
// If we want an object (on the screen or otherwise) to be notified about an event we must add a listener for that event to that object.
// We also need to specify what happens everytime the event we are listening for happens.
PowerButton.addEventListener(MouseEvent.CLICK, handleButtonClicks);
}
//This function is called when the oven on button recieves a click.
public function handleButtonClicks(event:MouseEvent):void
{
OvenOn.visible = true;
trace("the oven is being switched on");
}
}
}
The issue i keep getting is that OvenOn and PowerButton are giving me a undefined access issue and im not sure how to fix it. I have found posts on similar subjects like - Access of Undefined property? Actionscript 3
but im not quite sure how to apply it to my issue if anyone could offer any help that would be great.
When you're programming on the timeline, code is referencing the local namespace, and objects you make there (movieclips, textfields, etc.) are automatically instantiated in that namespace so that you can simply call OvenOn.visible = true. However, for each class, their local namespace is whatever is inside the class, so unless you actually created a property on your class called OvenOn, it will most definitely give you Access of Undefined Property errors.
Think of each class as its own island. For them to touch eachother, they need some sort of connection. That connection can be made once the parent instantiates the class in its own namespace. For example...
var foo:String = "Hello!";
var bar:MyClass = new MyClass();
// At this point, whatever code runs inside of MyClass has no concept of foo, or how to access it.
addChild(bar);
// Now that we've added it to the stage, the bar has some properties that have automatically been populated such as "root", "parent", or "stage".
foo.someProperty = "World";
// Since this namespace has a variable pointing to the instance, we can change properties on that class.
Now that we've instantiated MyClass on the stage, we can reference parent properties the class didn't know about. Mind you, this is not necessarily best practice.
package
public class MyClass extends MovieClip {
var someProperty:String = "cheese";
public function MyClass() {
trace(parent.foo) // this will fail
addEventListener(Event.ADDED_TO_STAGE, test);
}
public function test(e:Event):void {
trace(this["parent"].foo); // this will succeed
}
}
}
If you absolutely must change something that is not part of your Kitchen class, pass either the parent of OvenOn or that object specifically as a property of Kitchen. You could do this a couple ways.
with the Constructor...
var something:*;
public function MyClass(someObject:*) {
something = someObject;
}
public function test():void {
something.visible = false;
}
...or by Assigning the Property...
var bar:MyClass = new MyClass();
bar.something = OvenOn;
bar.test(); // will turn off the OvenOn now that 'something' is pointing to it.

Can I create EventListener in AS3 from one Object and remove it from another Object?

I spent lots of time trying to resolve this issue. So, basically I have an class (addadd) that contain function which can create eventListener and remove eventListener. And I have another two objects (Symbol1, Symbol2) that tell the function what to do - create or remove.
package {
import flash.display.*;
import flash.events.Event;
import fl.motion.MotionEvent;
import flash.events.MouseEvent;
public class addadd
{
var stanishev:stanishev_line = new stanishev_line;
public function addadd()
{
// constructor code
}
public function stanishevF(par1)
{
if (par1 == "create")
{
Main.display.addChild(stanishev);
stanishev.name = "stanishev_child";
stanishev.x = -200;
stanishev.y = 500;
stanishev.gotoAndPlay("start");
stanishev.addEventListener(Event.ENTER_FRAME, frameDOstanishev);
}
else
{
trace ("asasasas");
stanishev.removeEventListener(Event.ENTER_FRAME, frameDOstanishev);
}
}
public function frameDOstanishev(e:Event)
{
trace (stanishev.currentFrame);
}
} }
package {
import flash.display.SimpleButton;
import flash.events.MouseEvent;
public class Symbol1 extends SimpleButton
{
var call_creator:addadd = new addadd;
public function Symbol1()
{
// constructor code
addEventListener(MouseEvent.MOUSE_OVER, eventResponse);
addEventListener(MouseEvent.MOUSE_DOWN, eventResponse2);
}
function eventResponse(e:MouseEvent)
{
call_creator.stanishevF("create");
}
function eventResponse2(e:MouseEvent)
{
call_creator.stanishevF("destroy");
}
} }
package {
import flash.display.SimpleButton;
import flash.events.MouseEvent;
public class Symbol2 extends SimpleButton
{
var call_creator:addadd = new addadd;
public function Symbol2()
{
// constructor code
addEventListener(MouseEvent.MOUSE_DOWN, eventResponse2);
}
function eventResponse2(e:MouseEvent)
{
call_creator.stanishevF("destroy");
}
} }
So I can make addadd class to create and remove this eventListener from Symbol1 but I am not able to make the addadd class to create this eventListener sending "create" parameter from Symbol1 and remove it from Symbol2 by sending "destroy" parameter!!!
How can I create and remove the same eventListener from different objects? I found this approach creating and killing the listeners for more organized but I am not sure if this is the right way. I am having problems with the listeners (error: 1009) when I move between frames in the main timeline, so I want to kill them all before jump to another frame.
Thanks
You can put public function say, removeListeners() and addListeners() in that class and call those function from other class, like so,
class A ()
{
//constructor code
public function removeListeners():void
{
//remove listeners here
}
}
Then call this removeListeners() public function from the class(e.g. Main.as) where you have created instance of this "A" class.

How to improve this AS3 code structure to be more effective?

I have made an AS3 code to be a function. But I think my code is too lengthy. Could you help to improve it? Thank you!
I created test.fla first and added 5 grey block(external pictures from PSD) to stage. My function is to display different pictures when hovering mouse on corresponding grey block.
I converted those 5 grey blocks to Movie Clip and set instance name as sp1, sp2, sp3, sp4 and sp5. Then I created a document class, test.as and set 5 EventListener.
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse1);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse2);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse3);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse4);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse5);
So my first question is can I have any method to combine those 5 EventListener to be one? Because in my mind, so many EventListener will cost much more resource of PC.
My second question is I set 5 target pictures as 5 class.
In test.as I created code below:
public class EuroCup extends Sprite{
var arr:Array=new Array();
var Res1:Result609=new Result609();
var Res2:Result610=new Result610();
var Res3:Result611=new Result611();
var Res4:Result612=new Result612();
var Res5:Result613=new Result613();
var i:int=0;
public function EuroCup() {
arr[1]=Res1;
arr[2]=Res2;
arr[3]=Res3;
arr[4]=Res4;
arr[5]=Res5;
}
}
I think that is too lengthy. Is there any way to simplify it?
Here is the test.fla and test.as:Download
Whatever, thank u guys!
Restructuring:
public class EuroCup extends Sprite {
private var arr:Array;
public function EuroCup() {
arr = [ new Result609(), new Result610(),
new Result611(), new Result612(), new Result613()
];
}
}
Then use results as arr[0], arr[1] and so on. Also, if you have several sprites to listen clicks on, with similar listeners, you can connect all such sprites to single listeners and use event.target to distinguish them, where event is MouseEvent. Or place them into container and create one listener to that container - again, event.target will tell what sprite is clicked.
And yet two things - every time you see new Array(), replace it with [] - its faster and shorter. And place all code into constructor, not class body - it will be compiled to be executed faster.
You can/should use a Dictionary for associations between the grey rects and the images to display.
package {
public class EuroCup {
private var _children:Array, _current:Sprite, _map:Dictionary;
public function EuroCup() {
super();
initialize();
}
protected function initialize():void {
_children = [];
_map = new Dictonary();
// i don't know the image's symbol name.
// _map[_children[_children.length] = new Result609()] = new SYMBOL_NAME();
for each(var child:Sprite in _children) {
child.addEventListener(MouseEvent.CLICK, click_handler);
}
}
private function click_handler(event:MouseEvent):void
{
if (_current) {
_current.visible = false; // or use fading, etc
}
_current = _map[event.currentTarget] as Sprite;
if (_current) {
_current.visible = true; // or use fading, etc
}
}
}
}
One option for simplifying the code would be to associate the sp and Res instances with each other by identity, using a Dictionary. That allows you to avoid the work of tracking array indices, which is half of the reason you have separate event handler methods. Once the instances are associated by identity, then you can use the currentTarget property of a dispatched event to determine which element in the Dictionary you want to show on the stage.
package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.utils.Dictionary;
public class test extends Sprite
{
var dict:Dictionary = new Dictionary();
var visibleResult:MovieClip;
public function test()
{
dict[sp1]=new Result609();
dict[sp2]=new Result610();
dict[sp3]=new Result611();
dict[sp4]=new Result612();
dict[sp5]=new Result613();
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
}
private function clickmouse(evt:MouseEvent):void
{
if(visibleResult)
{
removeChild(visibleResult);
}
var Res:MovieClip = dict[evt.currentTarget] as MovieClip;
addChild(Res);
Res.x=300;
Res.y=400;
visibleResult=Res;
}
}
}
If you expect to have more than 5 sp instances in the application, then you could use a loop to assign the event listeners. But for less than 10 instances, you probably don't gain much from a loop.
I would go for a more simple version; add only one event listener and use Event.target to determine on which item is clicked, using a switch-statement.
This is helpful if the buttons should do different things.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public var sp1:Sprite;
public var sp2:Sprite;
public var sp3:Sprite;
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
trace("Clicked on: " + event.target)
switch (event.target)
{
case this.sp1:
{
// do something here
break;
}
case this.sp2:
{
// do something here
break;
}
case this.sp3:
{
// do something here
break;
}
default
{
trace("No handler defined for: " + event.target)
}
}
}
}
}
However, you can also make smart use of it's type. Let's say all you buttons extend a custom class called CustomButton, and they all need to do the same (like call a function), but with a parameter based on it's id.
This is helpful if the buttons should basically do the same thing.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
if (event.target is CustomButton)
{
var button:CustomButton = event.target as CustomButton; // you're now sure it's a CustomButton
this.showById(button.id); // let's say CustomButton has a public var 'id'
}
}
private function showById(id:int):void
{
// do something
}
}
}
Hope that helps.
Tip: Always start your class+filename with a capital. Variables start with capitals. This is very common in the actionscript world.

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 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