I'm trying to make a level select screen, and instead of copy and pasting a bunch of level select buttons onto my level select screen, I wanted to try to create and position the buttons using code for a little more flexibility in the future. I've run into a few problems while doing this...the main problem being that my custom navigation events, for whatever reason, fail to bubble up to my main document class from my button class.
I've used over 20 navigation events in my project, so I'm pretty certain the problem is not my method of doing navigation events as a whole, but the problem is specific to using a button class. Or something else I'm missing. Anyhow, I'll post the code below. I performed a few traces to try and figure out what was going on. My custom navigation events class is creating the proper event, then my button class is dispatching the event, but the the code that listens for this event in my document class never runs. So I'm assuming there's some kind of problem in bubbling the navigation event up to the top. That's my best guess, could be something completely different. If you guys also have different suggestions about how to make my level select screen work better by completely restructuring what I've done, that's fine too...but this has been somewhat of a mystery and I was hoping to get it working this way. Thanks a bunch, here's the relevant code!
---NOTE: If this helps, the hierarchy is DocumentClass --> TitleScreen --> LevelSelectScreen --> LevelSelectButtons
LevelSelectScreen class
public class LevelSelectScreen extends MovieClip {
public var levelSelectButtons:LevelSelectButtons;
public function LevelSelectScreen() {
for (var i:int = 1; i<=2; i++)
{
levelSelectButtons = new LevelSelectButtons(i);
addChild(levelSelectButtons);
}
}
}
LevelSelectButtons Class
public class LevelSelectButtons extends SimpleButton {
public var levelNumber:int;
public var levelSelectScreen:LevelSelectScreen;
public function LevelSelectButtons(i) {
x = 200;
y = 100 + 50*i;
addEventListener(MouseEvent.CLICK,LevelSelectClicked,false,0,true)
levelNumber = i;
}
public function LevelSelectClicked(mouseEvent:MouseEvent):void
{
if (levelNumber == 1)
{
dispatchEvent( new NavigationEvent( NavigationEvent.START ) );
}
if (levelNumber == 2)
{
dispatchEvent( new NavigationEvent( NavigationEvent.STAGE2 ) ); }
}
}
}
NavigationEvent class
package
{
import flash.events.Event;
public class NavigationEvent extends Event
{
public static const START:String = "start";
public static const LEVELSELECTSCREEN:String = "levelselectscreen";
public static const STAGE2:String = "stage2";
public function NavigationEvent( type:String, bubbles:Boolean = false, cancelable:Boolean = false )
{
super( type, bubbles, cancelable );
trace(type);
}
public override function clone():Event
{
return new NavigationEvent( type, bubbles, cancelable );
}
public override function toString():String
{
return formatToString( "NavigationEvent", "type", "bubbles", "cancelable", "eventPhase" );
}
}
}
If you want the events to bubble up you need to set the bubbles property to true.
dispatchEvent( new NavigationEvent(NavigationEvent.START, true) );
Related
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.
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.
I just started programming OOP and I'm running into a scope problem.
In the following project, I have a masterClass called App. The App-class has Screens:Screen-class and a Navigation-class as it's children. From the navigation class I want to control which screens will be displayed. I don't know how to do this...
Please check the code to fully understand my intentions
Your help is really appreciated, I'd love to really learn programming and not just a dirty solution :) but all suggestions are welcome!
// Main Class //
public class App extends Sprite
{
private var screens:Array;
private var screen1:Screen;
private var screen2:Screen;
private var screen3:Screen;
private var screen4:Screen;
public var currentScreen:String;
//
private var navigation:Navigation;
public function App()
{
init();
}
private function init():void {
buildScreens();
buildNavigation();
}
private function buildScreens():void {
screen1 = new Screen();
screen1.name = 'startScreen';
currentScreen = screen1.name;
addChild(screen1);
screen2 = new Screen();
screen2.name = 'irrelevantA';
screen3 = new Screen();
screen3.name = 'irrelevantB';
screen4 = new Screen();
screen4.name = 'irrelevantC';
screens = new Array(screen1, screen2, screen3, screen4);
}
private function buildNavigation():void {
navigation = new Navigation(screens);
}
}
// Screen Class //
public class Screen extends Sprite
{
public function Screen()
{
// creates a new screen
}
}
// Navigation Class //
public class Navigation extends Sprite
{
private var buttons:Array;
public function Navigation(screens:Array)
{
addButtons(screens);
}
private function addButtons(screens:Array):void {
buttons = new Array();
for each(var screen:Screen in screens) {
var button:Button = new Button();
button.link = screen.name;
button.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
buttons.push(button);
}
}
private function mouseDown(e:MouseEvent):void {
// THIS IS WHAT MY QUESTION IS ABOUT: How should I talk to the parent class in an OOP correct way?
// and how can I add and remove a screen in the App class from here?
// Here some of my tries
// I don't think using parent to get there is a good way because next time it might be; parent.parent.parent
trace(e.target.parent.parent.currentScreen);
this.parent.currentScreen;
stage.App.currentScreen;
App.currentScreen;
//---------------------------------
}
}
// Button Class //
public class Button extends Sprite
{
public var link:String;
public function Button()
{
// creates a new button
}
}
If you directly access parent classes from child objects, you create strong coupling - which is exactly what you don't want in a well-built system. It is best not to access the application object directly, but to use event listeners and custom events to promote changes from e.g. the navigation.
Here's an example. First, create a custom event:
public class MyCustomEvent extends Event {
public static const MENU_ITEM_SELECTED : String = "MENU_ITEM_SELECTED";
public var selectedItem:String;
}
Then, let the navigation dispatch it, when a button is clicked:
public class Navigation extends Sprite () {
// ...
private function onButtonClicked(ev:Event) : void {
ev.stopPropagation();
var custEvent:MyCustomEvent = new MyCustomEvent(MyCustomEvent.MENU_ITEM_SELECTED);
custEvent.selectedItem = ev.target.name;
this.dispatchEvent (custEvent);
}
// ...
}
Finally, let the application handle the custom event and bring up a different screen:
public class App {
// ...
public function createNavigation () : void {
navigation = new Navigation ();
navigation.addEventListener (MyCustomEvent.MENU_ITEM_SELECTED, onMenuItemSelected);
// ... more stuff happening
}
// ...
private function onMenuItemSelected (ev:MyCustomEvent) : void {
switchToScreen (ev.selectedItem);
}
private function switchToScreen (name:String) : void {
// choose screen by name, etc.
}
}
For all of this, neither the screen, nor the navigation have to know anything about any other objects involved, so you can easily replace each one without breaking the rest of the system.
You basically want to communicate downward (parent to child) by passing in references as arguments (as you're doing with the screens array) and upwards (child to parent) by calling public functions.
So, in your case something like this:
App class:
private function buildNavigation():void {
navigation = new Navigation(this, screens);
}
//etc
public function changeScreen(newScreen:int):void{
//Your logic for adding/removing screens goes here
}
Navigation class:
private var app:App
public function Navigation(app:App, screens:Array)
{
this.app = app
addButtons(screens);
}
private function addButtons(screens:Array):void {
buttons = new Array();
for each(var screen:Screen in screens) {
var button:Button = new Button();
button.link = screen.name;
button.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
buttons.push(button);
}
}
private function mouseDown(e:MouseEvent):void {
app.changeScreens(2);
}
}
Obviously, change the implementation to suit your needs (for example, now that you have a reference to the App class, consider if you need to pass in a separate reference to the screens array or not) - this is just an example of how you can communicate.
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
I tried to dispatch a custom event from some component on the stage and I registered another component to listen to it but the other component doesn't get the event.
Here is my code; what did I miss?
public class Main extends MovieClip //main document class
{
var compSource:Game;
var compMenu:Menu;
public function Main()
{
compSource = new Game;
compMenu = new Menu();
var mc:MovieClip = new MovieClip();
addChild(mc);
mc.addChild(compSource); // the source of the event - event dispatch when clicked btn
mc.addChild(compMenu); //in init of that Movie clip it add listener to the compSource events
}
}
public class Game extends MovieClip
{
public function Game()
{
btn.addEventListener(MouseEvent.CLICK, onFinishGame);
}
private function onFinishGame(e:MouseEvent):void
{
var score:Number = Math.random() * 100 + 1;
dispatchEvent(new ScoreChanged(score));
}
}
public class Menu extends MovieClip
{
//TextField score
public function Menu()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
//on init add listener to event ScoreChanged
addEventListener(ScoreChanged.SCORE_GAIN, updateScore);
}
public function updateScore(e:ScoreChanged):void
{
//it never gets here!
tScore.text = String(e._score);
}
}
public class ScoreChanged extends Event
{
public static const SCORE_GAIN:String = "SCORE_GAIN";
public var _score:Number;
public function ScoreChanged( score:Number )
{
trace("new score");
super( SCORE_GAIN, true);
_score = score;
}
}
I don't want to write in Main
compSource.addEventListener(ScoreChanged.SCORE_GAIN, compMenu.updateScore);
because I don't want compSource knowing about compMenu; it's compMenu's responsibility to know what events it needs to listen to.
Game and Menu appear to be on different chains in the chain of events. Events bubble upwards and since Game and Menu are siblings they will not have access to each other's events.
One solution would be for you to send reference of the game to the menu from the main screen. Then add an event listener to it from the menu at that point.
Sandro is correct, because events bubble up, not sideways, your Menu will never see the event.
A possible solution: as Main already "knows" about both compSource and compMenu you can safely pass the event through your main class:
class Main{
public function Main()
{
compSource = new Game();
compSource.addEventListener(ScoreChanged.SCORE_GAIN, scoreGainHandler);
compMenu = new Menu();
//... rest of constructor
}
public function scoreGainHandler(event:ScoreChanged):void
{
compMenu.updateScore(event);
}
//... rest of class
This way your Game and Menu stay independent.
In fact, if you build it this way, Menu doesn't need to listen to a score change event at all, you can just change the update function to take a score variable:
class Menu{
public function updateScore(score:int):void
{
tScore.text = String(score);
}
//... etc