How to correctly use KeyboardEvent on Flash - actionscript-3

I have a problem making a KeyboardEvent work in the game I'm starting. I have three classes, one for handling the levels, one that is the actual level and one to represent the avatar:
Level
import flash.display.MovieClip;
import flash.events.Event;
public class Fase extends Cena
{
var avatar:Avatar;
public function Fase()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
avatar = new Avatar();
this.addChild(avatar);
avatar.x = stage.width/2;
avatar.y = 30;
}
public function die()
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
(this.parent as ScreenHandler).removeChild(this);
}
}
Avatar
public class Avatar extends MovieClip
{
public function Avatar()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
//stage.focus=this;
this.addEventListener(KeyboardEvent.KEY_DOWN, apertou);
}
public function apertou(event:KeyboardEvent)
{
trace("o");
if(event.keyCode == Keyboard.LEFT)
{
this.x++;
}
}
}
I have all the packages on both classes an all works if I use the stage.focus=this on the Avatar, but if I click somewhere else during game excecution the focus is lost and it doesn't work anymore. Please can anyone help me?
Thanks in advance

Keyboard events only trigger when the object they're assigned to are the current focus.
Fortunately, the stage always has focus by default. This means you can add your event listeners to the stage to always have the keyboard events trigger as expected:
stage.addEventListener(KeyboardEvent.KEY_DOWN, apertou);

You can move the key handler from the avatar to the level or stage and then move your avatar in there.
public class Fase extends Cena
{
var avatar:Avatar;
public function Fase()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
avatar = new Avatar();
this.addChild(avatar);
avatar.x = stage.width/2;
avatar.y = 30;
addEventListener(KeyboardEvent.KEY_DOWN, apertou);
}
public function die()
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
(this.parent as ScreenHandler).removeChild(this);
}
public function apertou(event:KeyboardEvent)
{
if(event.keyCode == Keyboard.LEFT)
{
avatar.x++;
}
}
}

Related

ActionScript 3.0 Type Was Not Found Or Was Not A Compile-Time Constant

I just started programming a game and I when I ran my code it said
1064:Type Was Not Found Or Was Not A Compile-Time Constant:Event
1064:Type Was Not Found Or Was Not A Compile-Time Constant:Mouse Event
Here is the code:
package{
public class Script_1 {
public static const STATE_INIT:int = 10
public static const STATE_PLAY:int = 20
public static const STATE_GAME_OVER:int = 30
public var gameState:int = 0
public function gameLoop(e:Event):void{
switch(gameState) {
case STATE_INIT:
initGame();
break;
case STATE_PLAY:
playGame();
break;
case STATE_GAME_OVER:
gameOver();
break;
}
}
public function Game(){
addEventListener(Event.ENTER_FRAME, gameLoop);
gameState = STATE_INIT;
}
stage.addEventListener(MouseEvent.CLICK, onMouseClickEvent);
public function initGame():void{
stage.addEventListener(MouseEvent.CLICK, onMouseClickEvent);
clicks = 0
gameState = STATE_PLAY;
}
public function playGame(){
if (clicks >= 10){
gameState = STATE_GAME_OVER;
}
}
public function onMouseClickEvent(e:MouseEvent):void{
clicks++;
trace("mouse click number:" + clicks);
}
public function gameOver():void{
stage.removeEventListener(MouseEvent.CLICK, onMouseClickEvent);
gameState = STATE_INIT;
trace("game over");
}
}
}
This is in a file called Script_1.as
You need to import those classes with the import statement. This statement is required for each class that is missing and belongs above the class definition:
package
{
// Imports.
import flash.events.Event;
import flash.events.MouseEvent;
public class Script_1
{
// ..
}
}
Also, some misc things I noticed:
You're using addEventListener() but Script_1 does not extend EventDispatcher or at least implement IEventDispatcher. Based on the events you're trying to listen for, Sprite seems most suitable.
It looks like your class should either be Game or your constructor function Game() should be Script_1().

AS3 add an EnterFrame event listener to a Sprite subclass

I'm using AS3 with FlashDevelop
CustomClassA extends Sprite
CustomClassB extends CustomClassA but also needs to execute some code every frame
Is there any other way to do this, apart from making CustomClassA extend MovieClip instead of Sprite?
I'll try to create a simple example, using a dummy concept, only to explain a way how to do it. You could try something like:
public class CustomClassA extends Sprite
{
public function CustomClassA()
{
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
}
private function addedToStageHandler(event:Event):void
{
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
//start your code here...
}
public function startEnterFrame():void
{
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
}
public function stopEnterFrame():void
{
this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function enterFrameHandler(event:Event):void
{
executeSomeCodeEveryFrameMethod();
}
public function executeSomeCodeEveryFrameMethod():void
{
//your enter frame code...
}
public function dispose():void
{
stopEnterFrame();
//garbage collection...
}
}
and then, create your CustomClassB:
public class CustomClassB extends CustomClassA
{
public function CustomClassB()
{
}
override public function executeSomeCodeEveryFrameMethod():void
{
//custom executeSomeCodeEveryFrameMethod
}
}
you can test using:
var customClassB:CustomClassB = new CustomClassB();
customClassB.startEnterFrame();

Accessing different Classes in Actionscript 3

I am trying to program a little game but stumbled upon some problems.
I have several classes. One of them is the wall class. Which when hitting the player should remove a heart from the health bar.
Hitting the player works pretty good but I cant remove the heart which is inside the UI class.
Main.as
public class TwinRunner extends MovieClip
{
private var _timer:Timer;
private var _userInterface:UI = new UI();
public function TwinRunner()
{
//Timer initialize
_timer = new Timer(800, 1);
_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onUpdateTime);
_timer.start();
stage.addChild(_userInterface);
mcButton.addEventListener(MouseEvent.CLICK, onButtonClick);
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
Here I am adding the UI class as an instance.
Wall.as
x += _vx;
//Remove Wall when visible Sword hits
if(MovieClip(parent).mcSword.isVisible)
{
if(this.hitTestObject(MovieClip(parent).mcSword))
{
trace("wall destroyed");
parent.removeChild(this);
}
}
//Remove Wall when Player hit
else if(this.hitTestObject(MovieClip(parent).mcPlayer))
{
trace("player hit");
parent.removeChild(this);
MovieClip(parent)._userInterface.heartMeter = false;
}
//When the Wall is outside of the screen it gets removed
if (x + width / 2 < 0)
{
parent.removeChild(this);
}
}
MovieClip(parent)._userInterface.heartMeter = false; <- This part gives me an error...
And last the UI.as
public class UI extends MovieClip
{
private var heart1:heartFill = new heartFill();
public function UI()
{
addChild(heart1);
heart1.x = 200;
heart1.y = 200;
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(event:Event):void
{
addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onRemovedFromStage(event:Event):void
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
}
private function onEnterFrame(event:Event):void
{
}
public function set heartMeter(visibility:Boolean):void
{
heart1.visible = false;
}
}
I can access the setter function from the Main class but not from the wall.as class.
Is there a way to do so? I am still learning so I hope this is not such a stupid question :D
Thanks in advance!
Your problem is that you're removing the child (wall) from the parent, which then casuses the parent var to be null - see comments in code below:
parent.removeChild(this); //this will make parent null
MovieClip(parent)._userInterface.heartMeter = false; //parent is now null because this has been removed
Simply switching those two lines around should fix your main issue.
Also, _userInterface is declared as a private var in your main class, so if you want to access it in your wall class (like you are trying to do), you'll need to make it public or you'll get another error.
Something you may want to think about, is using events instead. That way, your code isn't directly referencing other classes like the UI. separation of class dependencies makes testing and troubleshooting easier.
So something like this in your wall class:
else if(this.hitTestObject(MovieClip(parent).mcPlayer))
{
trace("player hit");
stage.dispatchEvent(new Event(PLAYER_HIT)); //PLAYER_HIT would be a public static constant in this class: public static const PLAYER_HIT:String = "PlayerHit";
parent.removeChild(this); //this needs to be last because after this line, stage and parent will be null
}
Then in your UI Class:
private function onAddedToStage(event:Event):void
{
addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(Wall.PLAYER_HIT,playerHitHandler);
}
private function playerHitHandler(e:Event):void {
heartMeter = false;
}

how to add a limited amount of objects to the stage

So I have a box exported as Box in my library. I have tried :
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var _box:Box=new Box ;
private var boxAmount:Number=0;
private var boxLimit:Number=16;
private var _root:Object;
public function Main() {
addEventListener(Event.ENTER_FRAME, eFrame);
addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
}
private function eFrame(event:Event):void {
if (boxAmount <= boxLimit) {
boxAmount++;
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
} else if (boxAmount >= boxLimit) {
removeEventListener(Event.ENTER_FRAME, eFrame);
} else {
addEventListener(Event.ENTER_FRAME, eFrame);
}
}
}
}
But it did not work as planned.
What I am trying to do is make my box stay on the screen at a random place on the stage and remove it when clicked (but that will come later). This code is for some reason adding the object to the stage and then removing it and adding it again up to 16 times.
Thanks
I it seems like you have created one _box, and re-add it to the timeline on enter frame. It should work if you create a new box instance inside the eFrame function rather than before it, then you keep reassigning to the same variable name, rather than reusing the one object eg:
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var boxAmount:Number=0;
private var boxLimit:Number=16;
private var _root:Object;
public function Main() {
addEventListener(Event.ENTER_FRAME, eFrame);
addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
}
private function eFrame(event:Event):void {
if (boxAmount<=boxLimit) {
boxAmount++;
var _box:Box=new Box ;
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
} else if (boxAmount >= boxLimit) {
removeEventListener(Event.ENTER_FRAME, eFrame);
} else {
addEventListener(Event.ENTER_FRAME, eFrame);
}
}
}
}
In your code you are only ever creating one box. Your enterFrame handler is just assigning it a new random position 16 times. If you want 16 boxes you'll need to create a new box each time in the enterFrame function.
But you don't need to use the ENTER_FRAME event here. You could just use a for loop or a while loop to create the 16 boxes.
Here's some code:
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var boxAmount:Number=0;
private var boxLimit:Number=16;
public function Main() {
addBoxes();
}
private function addBoxes():void {
while (boxAmount<=boxLimit) {
boxAmount++;
var box:Box = new Box();
box.y=Math.random()*stage.stageHeight;
box.x=Math.random()*stage.stageWidth;
addChild(box);
// listen for mouse clicks
box.addEventListener(MouseEvent.CLICK, onBoxClick);
}
}
private function onBoxClick(e:MouseEvent):void {
var clickedBox:Box = e.target as Box;
removeChild(clickedBox);
}
}
}
I removed your enterFrame handler and just made a function called addBoxes. I'm using a while loop to crate the boxes. Notice that each time through the loop I'm creating a NEW box, not just reusing the old one. I'm also adding a mouse click event listener to each box so it can be removed from the stage when clicked.
You'll surely want to change some of this to get it to work for your purposes, but it should get you headed in the right direction.
What you have at the moment is just repositioning the same box over and over because you only ever create one Box instance. You need to create multiple instances of Box and add them to the stage individually.
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var boxAmount:Number=0;
private var boxLimit:Number=16;
private var _root:Object;
public function Main() {
addEventListener(Event.ENTER_FRAME, eFrame);
addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
}
private function eFrame(event:Event):void {
if (boxAmount<=boxLimit) {
boxAmount++;
//create a new box instance
var _box:Box = new Box();
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
} else {
removeEventListener(Event.ENTER_FRAME, eFrame);
}
}
}
}
Although the variable boxAmount suggests otherwise, you said you only want one box. So, to do this, you just need to move the following lines into the constructor (Main).
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
Then remove or disable the enter frame event. You don't need it in this case. To check if the box got clicked, attach the listener to the box itself, not to it's parent:
_box.addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
if (boxAmount<=boxLimit) {
// ...
} else if (boxAmount >= boxLimit) {
// ...
} else {
// ...
}
This part looks really strange. The first condition covers a case that is also covered by the second condition, together they already cover all possible cases. boxAmount is either less or equals to boxLimits or it is greater than it. Checking for equality twice is confusing. There is no need to include the last else statement. It actually has the same behaviour as the following code.
if (boxAmount<=boxLimit) {
// ...
} else if (boxAmount > boxLimit) {
// ...
}

Strange behavior of Event listener / Event flow

In the following code, if I click on the 'button', all three function will be called.
But in all other cases, only stage event are fired.
Why the 'sprite' event didn't got fired?
public class EventFlowTest extends Sprite
{
private var button:Sprite;
public function EventFlowTest()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
stage.addEventListener(MouseEvent.MOUSE_DOWN,stageMouseDown,false);
graphics.beginFill(0x11);
graphics.drawCircle(100,100,100);
addEventListener(MouseEvent.MOUSE_DOWN,spriteMouseDown,false);
button=new Sprite();
addChild(button);
button.graphics.beginFill(0xF1);
button.graphics.drawCircle(100,100,10);
button.addEventListener(MouseEvent.MOUSE_DOWN,buttonMouseDown,false);
}
private function spriteMouseDown(e:MouseEvent):void
{
trace("sprite");
}
private function stageMouseDown(e:MouseEvent):void
{
trace("stage");
}
private function buttonMouseDown(e:MouseEvent):void
{
trace("button");
}
}
The explanation is "Vector graphics ignored in main class instance" (mouse interactions)
http://books.google.ru/books?id=gUHX2fcLKxYC&lpg=PA533&ots=cvPZ0qbQv8&dq=Vector%20graphics%20ignored%20in%20main-class%20instance&pg=PA533#v=onepage&q=Vector%20graphics%20ignored%20in%20main-class%20instance&f=false
that's strange... this behavior occur when your test class is the document class.
If you embed your test in a document class, every thing is running as expected. I have no explaination for that behavior.
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
var test : EventFlowTest = new EventFlowTest();
addChild(test);
}
}
}