Flash AS3 KeyboardEvent not firing - actionscript-3

I have a class like this:
public class GameOverScreen extends MovieClip {
public function GameOverScreen(useMouseControl:Boolean) {
if(useMouseControl){
Mouse.show();
restartButton.addEventListener(MouseEvent.CLICK, onClickRestart);
}
else{
this.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace);
}
}
public function onClickRestart(mouseEvent:MouseEvent):void{
dispatchEvent(new NavigationEvent(NavigationEvent.RESTART));
}
public function onPushSpace(keyboardEvent:KeyboardEvent):void{
trace(keyboardEvent);
dispatchEvent(new NavigationEvent(NavigationEvent.RESTART));
}...
It's an ending screen of a game. (surprise!) I want it to restart my game if I push down the space button, or click on the restartButton on the screen. As you can see the screen gets a boolean value in the constructor, wich decide that we're using keyboard or mouse to control the game. It works well with mouse, but with the key, i have to click on the restart button (wich is on the screen), till it does nothing, and after clicking it, and I push a button I get the playScreen, but my keylistener is somewhy still in work, and if i push any key, it restarts the game.
The point of my main class is: if the player dies, he get a gameOverScreen, and the playscreen will be dismissed, the gameOverScreen also gets a listener, it listens for an event called RESTART, if the event is dispatched, a new playScreen is created, and the game over dismissed.
public class Avoider extends MovieClip { ....
public function onAvatarDeath(avatarEvent:AvatarEvent):void {
var finalScore:Number = playScreen.getFinalScore();
var finalTime:Number = playScreen.getFinalTime();
gameOverScreen = new GameOverScreen(useMouseControl);
gameOverScreen.addEventListener(NavigationEvent.RESTART, onRequestRestart);
gameOverScreen.setFinalScore(finalScore);
gameOverScreen.setFinalTime(finalTime);
addChild(gameOverScreen);
playScreen = null;
}
public function restartGame():void {
playScreen = new PlayScreen(useMouseControl);
playScreen.addEventListener(AvatarEvent.DEAD, onAvatarDeath);
addChild(playScreen);
gameOverScreen = null;
}
public function onRequestRestart(navigationEvent:NavigationEvent):void {
restartGame();
}
I hope it's understandable, if not please note it what is not clean.
Thanks
UPDATE
my onAddToStage function
public function onAddToStage(event: Event):void{
stage.focus = this;
this.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace);
}

Try adding your key listener to the stage:
stage.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace);
Otherwise your current class needs to be in focus, which is why only works until you've clicked it. Be sure to remove that listener when your game over screen is done though.
Alternatively you could give your game over screen focus through code when it loads (in the constructor):
public function GameOverScreen(useMouseControl:Boolean) {
this.addEventListener(Event.ADDED_TO_STAGE,addedToStage,false,0,true);
if(useMouseControl){
Mouse.show();
restartButton.addEventListener(MouseEvent.CLICK, onClickRestart, false, 0, true);
}
else{
this.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace, false, 0, true);
}
}
private function addedToStage(e:Event):void {
stage.focus = this;
stage.stageFocusRect = false; //make sure there's no dumb yellow rectangle
}
Also a little tip - I notice your not removing your game over screen from the display list once it's finished. You'll want to do that to make it truly go away (and remove your restart event listener).
public function restartGame():void {
playScreen = new PlayScreen(useMouseControl);
playScreen.addEventListener(AvatarEvent.DEAD, onAvatarDeath);
addChild(playScreen);
gameOverScreen.removeEventListener(NavigationEvent.RESTART, onRequestRestart);
removeChild(gameOverScreen);
gameOverScreen = null;
}

Related

Dispatchevent not firing

I have a fla file and 2 class files. On my fla I have:
addEventListener(SubtitleLoadEvent.PASS_PARAMS, onProcessedEvent);
function onProcessedEvent(e:Event):void {
trace(e.currentTarget);
}
SubtitleLoadEvent.as :
package
{
import flash.events.Event;
public class SubtitleLoadEvent extends Event
{
public static const PASS_PARAMS:String = new String("passparams");
public var resultArr:Array = new Array();
public function SubtitleLoadEvent(type:String, arr:*, bubbles:Boolean = false,
cancelable:Boolean = false):void
{
this.resultArr = arr;
super(type, bubbles, cancelable);
}
override public function clone():Event
{
return(new SubtitleLoadEvent(type, resultArr, bubbles, cancelable));
}
}
}
And I have a class file which extends sprite :
dispatchEvent(new SubtitleLoadEvent(SubtitleLoadEvent.PASS_PARAMS, cleanArr));
But the movie doesn't output anything. How can I fix this?
Since your event doesn't bubble, the only way your timeline code will hear the event is if it was dispatched on the same scope (which is unlikely to be the case here).
If your dispatching sprite is on the same scope (timeline) or a descendant/child of it, then making the event bubble (third parameter when creating the event) should make it work. (or you could listen on the capture phase)
Otherwise, you will need to listen for the event on some common parent of both objects.
The easiest way to resolve this, is to dispatch and listen on the stage:
stage.addEventListener(SubtitleLoadEvent.PASS_PARAMS, onProcessedEvent);
stage.dispatchEvent(new SubtitleLoadEvent(SubtitleLoadEvent.PASS_PARAMS, cleanArr));
This assumes that your dispatching sprite has been added to the display list. If not, then the stage property will be null. If it's not on the display list, then it will not work.
TO learn more about how events work and their lifecycle, you could read this article.

object's stage reference lost when re-instantiated after being removed from memory & displaylist

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.

AS3 - dispatch custom event with parameter (not working)

I read a lot of topics about dispatching event but I can't make my code work.
This topic and this topic are closed to what I would like to do, but it's not working in my case.
Here is the situation :
My scene is a battle field and has two ships
Each ship knows when it is touched by a fire, so it has to inform the scene that contains the graphic interface
So the ship dispatch a custom event with itself as parameter, so the scene knows when a ship is touched and which ship
I have 3 classes :
The custom event class is an event that has a property "Ship"
The EventDispatcher class
The symbole class that corresponds to my scene and listen to the event
1) CustomEvent class
public class FightEvent extends Event
{
public static const SHIP_TOUCHED:String = "SHIP_TOUCHED"; //type
public var object:Ship = null; //object to pass
public function FightEvent(type:String, pObject:Ship, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
object = pObject;
}
public override function clone():Event
{
return new FightEvent(type, object, bubbles, cancelable);
}
}
2) EventDispatcher
public class Ship extends EventDispatcher
{
private function updateDamages():void
{
//compute damages
dispatchEvent( new FightEvent( FightEvent.SHIP_TOUCHED, this ) );
}
}
3) Scene
public class Fight extends customMovieClip
{
private var playerShip:Ship; //I have two ships, player and enemy
private var enemyShip:Ship;
public function init():void
{
stage.addEventListener(FightEvent.SHIP_TOUCHED, onShipTouched);
//I made a test : the event listener is correctly added
}
private function onShipTouched(e:FightEvent):void
{
//update the graphic interface to show damages
}
}
My event listener is added, the code passes on the dispatch line, but onShipTouched is not called.
Please help, what did I miss ?
What is the element I didn't understand ?
Is it a good way to use events like this ? or should I set a reference to the scene inside the Ship class ?
Your Ship class is not a MovieClip, Sprite or their subclass in order for your event to bubble up the display list, just because they don't participate in a display list. So, you change your Ship to Sprite subclass (this one already inherits EventDispatcher), add graphics and addChild() both ships to the stage, this way your stage will receive events properly.
My logic was wrong :
I was doing this : the eventdispatcher is dispatching the event, and the stage is listening to the event. That is incorrect.
The right solution is : the eventdispatcher is dispatching the event, and is also listening to its own dispatched event. The eventlistener is added to the eventdispatcher in the scene, that contains the function to call.
As Vesper said, the parameter in FightEvent is useless, as the eventdispatcher is actually the event target.
DodgerThud, BotMaster, you gave me this answer, thank you for your help (Looks like I can't set a comment as "answer accepted").
Here the right code for the Fight class :
public class Fight extends customMovieClip
{
private var playerShip:Ship; //I have two ships, player and enemy
private var enemyShip:Ship;
public function init():void
{
enemyShip.addEventListener(FightEvent.SHIP_TOUCHED, onShipTouched);
playerShip.addEventListener(FightEvent.SHIP_TOUCHED, onShipTouched);
}
private function onShipTouched(e:FightEvent):void
{
//e.target == the ship that dispatched the event
}
}

Cant access nested MovieClip AS3

I'm trying to create a intro screen then a start screen after the intro screen is done playing.
I thought the easiest way of doing this would be on scene 1 frame 1, I would create a
MovieClip.
By the way this is a separate document file. So I gave it a document class name of mcStartGameScreen and linked it to Flash Develop for actions.
Now the MovieClip that is on frame 1, I gave an instance name of startMenu then inside the startMenu MovieClip there is a MovieClip that I wanted the buttonMode enabled to be true. I add this MovieClip which is called mcStart on frame(65) inside my startMenu.
Now in my Actions I have this:
public class mcStartGameScreen extends MovieClip
{
private var mcStart:MovieClip;
private var startMenu:MovieClip;
public function mcStartGameScreen()
{
startMenu.mcStart.buttonMode = true; //This is giving me the ERROR!
mcStart.addEventListener(MouseEvent.CLICK, startOnClick);
}
private function startOnClick(e:MouseEvent):void
{
dispatchEvent(new Event("START_GAME"));
}
public function hideScreen():void
{
this.visible = false;
}
public function showScreen():void
{
this.visible = true;
}
}
When I test the movie I get this
error: Cannot access a property or method of a null object reference.
Does anyone know what I am doing wrong?
If you already have a MovieClip with instance name startMenu placed on the stage, so no need for,
private var startMenu:MovieClip; you remove this from your code.
And always have the stage instance first and then proceed.
So modify your constructor like so:
public function mcStartGameScreen()
{
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void
{
startMenu.mcStart.buttonMode = true; //Now this will not give the ERROR!
startMenu.mcStart.addEventListener(MouseEvent.CLICK, startOnClick);
}

event flow in action script 3

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