actionscript 3 Timer class and callback problems - actionscript-3

I have a timer class in actionscript that looks like this:
public class AGTimer {
public var myTimer:Timer ;
public var done:Boolean = false;
public var timer_disable:Boolean = false;
public var started:Boolean = false;
public function AGTimer(num:Number = 0) {
this.timer_disable = false;
if (num != 0 ) timerStart(num);
// constructor code
}
public function timerStart(num:Number):void {
myTimer = new Timer(num * 1000, 1);
myTimer.addEventListener(TimerEvent.TIMER, runOnce);
myTimer.start();
started = true;
done = false;
}
public function runOnce(e:TimerEvent):void {
done = true;
}
public function timerDone():Boolean {
if (done && ! timer_disable) return true;
else if (!started ) return true;
else return false;
}
}
I have several of these in an array. I reference them with their index number. sometimes I start a timer and let it go, then before I have a chance to check it, I delete it by calling 'new AGTimer()' and creating a new one. Could it happen that the callback from the actionscript Timer object is still called somehow after the timer itself is deleted? What would I do about it if it did happen? I am trying to find a bug and am considering this sort of problem. What kind of error would I see?

Yes, a timer will continue to run callbacks even if it is not referenced somewhere. You have to remove event listener for the timer for it in order to remove it.
Basically something like this:
private function destroyTimer():void {
if(myTimer) {
myTimer.removeEventListener(runOnce);
myTimer.stop();
myTimer = null;
}
}

Related

AS3 Jumping (flying) issue

Ive searched around a bit but I couldnt find an answer I can work with.I probably didnt look hard enough. I have been out of school for about a year now and I came across a flash game we made in class. I was trying to fix some errors on it that Ive made to make it function properly. The issues Ive come across are a Jumping issue : The character can endlessly jump as if it is flying through the sky. The next error is the attack graphic doesnt play unless the player is in the sky. Im rusty with the coding and I am looking to get back into as3 and using the flash program. I have a "player.as" file, I will paste its code below.
Any help is appreciated, thanks in advance.
package {
import flash.display.MovieClip;
import CollisionObject;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Player extends CollisionObject {
private var xMovement:Number;
public var isAttacking:Boolean;
private var attackTimer:Timer = new Timer (500, 1);
public function Player() {
// constructor code
trace("I am the player");
xMovement = 0;
isAttacking:false;
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
} // end constructor
private function enterFrameHandler(event:Event):void{
this.x += xMovement;
} //end function
public function attack() {
isAttacking = true;
this.gotoAndStop("attack");
attackTimer.addEventListener(TimerEvent.TIMER_COMPLETE, doneAttacking);
attackTimer.start();
}
public function startJumping(){
if (isJumping == false) {
isJumping == true;
this.gotoAndStop("jump");
downwardVelocity = -20;
}
}
public function doneAttacking (event:TimerEvent):void{
isAttacking = false;
this.gotoAndStop("stop");
}
public function moveLeft() : void{
xMovement =-7;
this.scaleX = -1;
this.gotoAndStop("run");
isRunning = true;
} //end function
public function moveRight() : void{
xMovement =7;
this.scaleX = 1;
this.gotoAndStop("run");
isRunning = true;
} //end function
public function standStill() : void{
xMovement = 0;
isRunning = false;
} //end function
override public function positionOnLanding(){
if(isRunning == true){
this.gotoAndStop("run");
}else{
this.gotoAndStop("stop");
} //end else
} //end function
} // end class
Your player is jumping endlessly because you never set the iSJumping to true. It should be
isJumping = true;
instead of
isJumping == true;
Attack is probably not working because you have an own frame for the attack and the frame is probably reset by the running or positionOnLanding ?
And please move your attackTimer event listener into the constructor, now a new event listener is created each time you are attacking, this leads to a memory leak and there is really no reason to do so.

addChild not displaying anything on stage

So I'm trying to make a spaceship fire lasers when the spacebar is pressed. I've done this before in a pure flex project but have recently gotten creative cloud and am trying to recreate the same effect using flash professional/flash builder.
Unfortunately when I create a new instance of my "Laser" class and try and put it on the stage with addChild() nothing seems to happen.
Here is the main file/document class
public class PlayerShip extends Sprite
{
private var laserTimer:Timer;
private var shipTime:Timer;
private var upKey:Boolean;
private var downKey:Boolean;
private var leftKey:Boolean;
private var rightKey:Boolean;
private var spacebar:Boolean;
private var utils:Utils = new Utils();
//tuning variables
private var MOVE_SPEED:int = 5;
private var REVERSE_SPEED:int = 3;
private var TURN_SPEED:int = 5;
private var laserEmitter:shipLasers = new shipLasers(stage);
public function PlayerShip():void
{
super();
addEventListener(Event.ENTER_FRAME, fly);
stage.addEventListener(KeyboardEvent.KEY_DOWN, movementKeysDown);
stage.addEventListener(KeyboardEvent.KEY_UP, movementKeysUp);
laserTimer = new Timer(1000/1000);
laserTimer.addEventListener(TimerEvent.TIMER, fireLasers);
laserTimer.start();
addChild(laserEmitter);
}
public function fly(e:Event):void {
if(downKey) {
SpaceShip.x -= Math.sin(utils.degreesToRadians(SpaceShip.rotation)) * REVERSE_SPEED;
SpaceShip.y += Math.cos(utils.degreesToRadians(SpaceShip.rotation)) * REVERSE_SPEED;
}
if(upKey) {
SpaceShip.x += Math.sin(utils.degreesToRadians(SpaceShip.rotation)) * MOVE_SPEED;
SpaceShip.y -= Math.cos(utils.degreesToRadians(SpaceShip.rotation)) * MOVE_SPEED;
}
if(leftKey) {
SpaceShip.rotation -= TURN_SPEED;
}
if(rightKey) {
SpaceShip.rotation += TURN_SPEED;
}
}
public function movementKeysUp(e:KeyboardEvent):void { //rotators is key_up :P
switch(e.keyCode) {
case 83:
downKey = false;
break;
case 65:
leftKey = false; // on "a" key_up sets left turn to false. Simple enough.
break;
case 68:
rightKey = false; // K. "d" released makes me not turn right.
break;
case 87:
upKey = false; // I guess case 87 is "w"
break;
case 32:
spacebar = false;
break;
}
}
public function movementKeysDown(e:KeyboardEvent):void { // key_down for movers
switch(e.keyCode) {
case 83:
downKey = true;
break;
case 65:
leftKey = true; //so now on key_down for the "a" key it makes me go left! :D
break;
case 68:
rightKey = true; //same as lft...
break;
case 87:
upKey = true;
break;
case 32:
spacebar = true;
break;
}
}
public function fireLasers(e:TimerEvent) {
if(spacebar) {
laserEmitter.Emit(SpaceShip.x, SpaceShip.y, SpaceShip.rotation);
addChild(laserEmitter);
}
}
public function getShip():MovieClip {
return SpaceShip;
}
}
}
and this is the separate class that is supposed to create new instances of the Laser class and put them on the stage.
public class shipLasers extends Sprite implements Emittable
{
var tempLaserRight:MovieClip;
var tempLaserLeft:MovieClip;
var laserArray:Array = [];
public function shipLasers(stage:Stage):void
{
}
public function Emit(x:int, y:int, rotation:Number):void {
tempLaserRight = new Laser();
tempLaserLeft = new Laser();
tempLaserRight.rotation = tempLaserLeft.rotation = rotation;
tempLaserRight.x = 200;
tempLaserLeft.x = 210;
tempLaserRight.y = 200;
tempLaserLeft.y = 200;
laserArray.push(tempLaserRight);
laserArray.push(tempLaserLeft);
stage.addChild(tempLaserRight);
stage.addChild(tempLaserLeft);
trace("Oh come on!");
}
}
}
Thanks!
You never store passed stage reference in shipLasers class, thus you are trying to refer its own built-in stage ref, which is likely null because your instances of shipLasers don't get added to display list themselves. You need to store the stage ref passed in the constructor and use that to add children.
public class shipLasers ... {
var theStage:Stage;
public function shipLasers(aStage:Stage){
theStage = aStage;
}
public function Emit(...) {
...
theStage.addChild(tempLaserRight);
thestage.addChild(tempLaserLeft);
}
}
Update: It's also a good practice to first check stage availability, then use stage reference. To do this, you need to listen to Event.ADDED_TO_STAGE event in your ship class, as it uses stage left right and center. The common code of achieving this is as follows:
public function PlayerShip() {
....
// leave here only code that does not require stage
if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE,init);
}
private function init(e:Event=null):void {
removeEventListener(Event.ADDED_TO_STAGE,init);
// here place all code that's left from your initialization process
laserEmitter=new shipLasers(stage);
stage.addEventListener(KeyboardEvent.KEY_DOWN, movementKeysDown);
stage.addEventListener(KeyboardEvent.KEY_UP, movementKeysUp);
// etc.
}

My Character Won't Move :( Multiple .as files AS3

I have a few .as files. They are: MainClass.as, FrontEnd.as, Levels.as, and Hero.as. My problem (as far as I know) is in my Hero.as file. Let me descibe how I have it all set up thusfar because I have been a bit concerned that there are better ways of doing things in AS3.
MainClass.as makes a variable of FrontEnd (menus, namely the main menu) and calls it up (addChild).
FrontEnd.as are my menus. buttons and whatnot...
Levels.as right now just calls up level 1 when the start new game button is pressed on the main menu. Had one hell of a time figuring out how to use functions from a different .as file. Hero.as I will add my code for. I'm posting the whole thing because I don't know where my problem is.
public class Hero extends MovieClip
{
public var roger:player = new player();
private var animationState:String = "down";
public var facing:String = "down";
private var isLeft:Boolean = false;
private var isRight:Boolean = false;
private var isUp:Boolean = false;
private var isDown:Boolean = false;
public var currentPlayer:MovieClip = roger;
public function Hero()
{
addEventListener(Event.ENTER_FRAME, loop);
addEventListener(Event.ADDED_TO_STAGE, onStage);
trace(currentPlayer);
}
public function onStage( event:Event ):void
{
removeEventListener( Event.ADDED_TO_STAGE, onStage );
}
public function addCurrentPlayer():void
{
roger.x = stage.stageWidth * .5;
roger.y = stage.stageHeight * .5;
addChild(roger);
currentPlayer = roger;
setBoundaries();
}
public function keyDownHandler(event:KeyboardEvent)
{
if (event.keyCode == 39)//right press
{
isRight = true;
}
if (event.keyCode == 37)//left pressed
{
isLeft = true;
}
if (event.keyCode == 38)//up pressed
{
isUp = true;
}
if (event.keyCode == 40)//down pressed
{
isDown = true;
}
}
public function keyUpHandler(event:KeyboardEvent)
{
if (event.keyCode == 39)//right released
{
isRight = false;
}
if (event.keyCode == 37)//left released
{
isLeft = false;
}
if (event.keyCode == 38)//up released
{
isUp = false;
}
if (event.keyCode == 40)//down released
{
isDown = false;
}
}
public function loop(Event):void
{
if (currentPlayer == null)
{
addCurrentPlayer();//make sure at least roger is on the screen
}
currentPlayer.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
currentPlayer.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
//----------------------------------0
//Animation States
//----------------------------------0
if (isDown == true)
{
currentPlayer.y += 5;
animationState = "walk_down";
facing = "down";
currentPlayer.gotoAndStop(animationState);
}
else if (isUp == true)
{
currentPlayer.y -= 5;
animationState = "walk_up";
facing = "up";
currentPlayer.gotoAndStop(animationState);
}
else if (isRight == true)
{
currentPlayer.x += 5;
animationState = "walk_right";
facing = "right";
currentPlayer.gotoAndStop(animationState);
}
else if (isLeft == true)
{
currentPlayer.x -= 5;
animationState = "walk_left";
facing = "left";
currentPlayer.gotoAndStop(animationState);
}
//----------------------------------0;
//IDLE STATES
//----------------------------------0
else if (isDown == false)
{
currentPlayer.gotoAndStop(facing);
}
else if (isUp == false)
{
currentPlayer.gotoAndStop(facing);
}
else if (isRight == false)
{
currentPlayer.gotoAndStop(facing);
}
else if (isLeft == false)
{
currentPlayer.gotoAndStop(facing);
}
}
public function setBoundaries():void
{
var halfHeight:int = currentPlayer.height * .5;
var halfWidth:int = currentPlayer.width * .5;
if(currentPlayer.y <= 1)
{
currentPlayer.y += halfHeight;
}
else if(currentPlayer.y > stage.stageHeight)
{
currentPlayer.y -= halfHeight;
}
else if(currentPlayer.x <= 1)
{
currentPlayer.x += halfWidth;
}
else if(currentPlayer.x > stage.stageWidth)
{
currentPlayer.x -= halfWidth;
}
}
}
}
trace(currentPlayer); is giving me [object player] instead of the instance name "roger". (Later on I want more playable characters.) I'm not sure if the problem is there or in my levels file, which I'll post here. (not as long as Hero.as)
public class Levels extends MovieClip
{
var currentLevel:MovieClip;
public function Levels()
{
addEventListener(Event.ADDED_TO_STAGE, onStage);
}
private function onStage(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, gotoLevelOne);
}
public function gotoLevelOne():void
{
var levelOne:LevelOne = new LevelOne();
var hero:Hero = new Hero();
addChild(hero);
levelOne.x = stage.stageWidth * .5;
levelOne.y = stage.stageHeight * .5;
addChild(levelOne);
currentLevel = levelOne;
hero.currentPlayer.x = 100;
hero.currentPlayer.y = 100;
addChild(hero.currentPlayer);
}
}
}
If I remove = roger; from var currentPlayer:MovieClip = roger; it gives me #1009 null object even though I told it in addCurrentPlayer() to change currentPlayer to roger. On level 1, everything shows up but I can't move my character. I know that it worked when I was working on his animations and I would call him to the main menu. Everything worked on him. What's the problem now?
Firstly, there's a lot of things wrong with your code:
In your Hero Class, the 'onStage' Event handler doesn't actually do anything other than remove the event listener that triggers it. While it's good practice to remove the event listener, there should be some other purpose to the Event handler. If there isn't you can remove it and not bother listening for the ADDED_TO_STAGE Event.
Similarly, in your Levels Class 'onStage' Event handler you attempt to remove the event, but name the wrong handler. I assume you want to remove the event handler and then run the 'gotoLevelOne' method. If so, just have the Event.ADDED_TO_STAGE listener call 'gotoLevelOne' and then remove the Event listener there:
public function Levels()
{
addEventListener(Event.ADDED_TO_STAGE, gotoLevelOne);
}
public function gotoLevelOne():void
{
removeEventListener(Event.ADDED_TO_STAGE, gotoLevelOne);
// class continues....
OK, so to your question:
You will be getting the null error because you are referring to currentPlayer from outside the Hero Class, before calling addCurrentPlayer (where it is set).
If you temporarily define currentPlayer as a private variable, the compiler should give you a line number where you first refer to currentPlayer from OUTSIDE the Hero Class (the error will be something like 'Class X is trying to access a private (or non-existent) property of Y Class').
You can then sort out WHY you are accessing currentPlayer before calling addCurrentPlayer. You may also want to think about if currentPlayer NEEDS to be public (if so, then what is 'addCurrentPlayer' for? That function effectively works as a setter for currentPlayer).
EDIT:
At the moment you are adding new KEY_DOWN and KEY_UP event listeners EVERY frame of your game loop. This is unnecessary. Add them both ONCE in the initialisation of your game (perhaps in your Hero's onStage handler), and count on them to trigger the appropriate handlers.
You will want to add the KEY_DOWN and KEY_UP listeners to 'stage', not currentPlayer, so:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
You will need to add the listeners AFTER your Hero instance has been added to the Stage though, so it has access to 'stage'. That's why it makes sense to add the listeners in the Hero's onstage handler.

How to change cursor on Roll Over event

I'm making a point and click game in AS3 with flash.
I've changed the skin of my cursor by creating a new class "Souris". It's working just fine. Now I'm trying to change the skin of the cursor when it's on an object on the scene.
I've read that the MouseEvent.ROLL_OVER is the good method but I can't figure out how to do it...
I've got my Souris class like that :
public class Souris extends MovieClip
{
private var engine:Engine;
private var stageRef:Stage;
private var p:Point = new Point();
public function Souris(stageRef:Stage)
{
Mouse.hide(); //make the mouse disappear
mouseEnabled = false; //don't let our cursor block anything
mouseChildren = false;
this.stageRef = stageRef;
x = stageRef.mouseX;
y = stageRef.mouseY;
stageRef.addEventListener(MouseEvent.MOUSE_MOVE, updateMouse, false, 0, true);
stageRef.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler, false, 0, true);
stageRef.addEventListener(Event.ADDED, updateStack, false, 0, true);
stageRef.addEventListener(MouseEvent.ROLL_OVER,hover);
}
private function updateStack(e:Event) : void
{
stageRef.addChild(this);
}
private function hover(e:MouseEvent):void {
souris.visible = false;
}
private function mouseLeaveHandler(e:Event) : void
{
visible = false;
Mouse.show(); //in case of right click
stageRef.addEventListener(MouseEvent.MOUSE_MOVE, mouseReturnHandler, false, 0, true);
}
private function mouseReturnHandler(e:Event) : void
{
visible = true;
Mouse.hide(); //in case of right click
removeEventListener(MouseEvent.MOUSE_MOVE, mouseReturnHandler);
}
private function updateMouse(e:MouseEvent) : void
{
x = stageRef.mouseX;
y = stageRef.mouseY;
e.updateAfterEvent();
}
}
}
}
In my main class (Engine class) I've got :
private var souris:Souris;
public function Engine(){
souris = new Souris(stage);
stage.addChild(souris);
}
private function startGame(e:Event):void{
....
..
I've tried to put in the "Souris" class
stageRef.addEventListener(MouseEvent.ROLL_OVER,hover);
private function hover(e:MouseEvent):void {
Engine.souris.visible = false;
handCursor.visible = true ;
}
But it seems wrong...
I don't know what to put in my hover function. (I've got the "handCursor" in my library).
Thank you very much for your help!
If you have "handCursor" in your library, then you need to assign a class to it, like 'HandCursor'. I advise that classes start with an uppercase letter.
So your code would need to create a new instance of it then show it, like
var handCursor:HandCursor = new HandCursor; handCursor.visible = false;
handCursor.visible = false; makes it not visible, then to make it visible, you would:
handCursor.visible = true;
Also, handCursor is a local variable if put in a function, to make it global to use in all functions, you would need to put it at the beginning of your Class.
Also, are you getting any errors? If so, please share them.

Making Timers available for GC?

In a function that creates a new timer every time it is called, is this the correct way to dispose of it?
private var _timers:Vector.<Timer> = new Vector.<Timer>;
private var _timer:Timer
private function timer():void
{
_timer = new Timer(10000, 1);
_timers.push(_timer);
_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer, false,0,true);
_timer.start();
}
private function onTimer(e:TimerEvent):void
{
e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
_timers[0] = null;
_timers.shift();
}
Maybe you can add a
_timers[0].stop();
Just in case your times change for whatever reason, it's best to deal with the object directly instead of assuming the Timer object is at the 0 index.
Just a small adjustment required for onTimer():
private function onTimer(e:TimerEvent):void
{
var timer:Timer = e.currentTarget as Timer;
timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
_timers.splice(_timers.indexOf(timer), 1);
}
You don't need to set the index to null and it's never good practice to assume your item will be at a specified index, always make sure. In this case you make sure by using the indexOf() method available in the Array type object (_timers).
Hope it helps.
I've made function "delay" that handles delay calls. I made it for my game in which I have used delay calls alot and I had to find a way to handle delays efficiently. Still AS3 guru's out there may still find more efficient ways, please let know if any.
public static var timer_stack:Vector.<Timer> = new Vector.<Timer>();
public static function delay(delaytime:Number, func_name:Function, repeat:Number = 1)
{
var timer:Timer = new Timer(delaytime, repeat);
timer_stack.push(timer);
timer_stack[timer_stack.length-1].addEventListener(TimerEvent.TIMER, func_name ,false,0,true);
timer_stack[timer_stack.length-1].addEventListener(TimerEvent.TIMER_COMPLETE,
function(e:TimerEvent){ delay_complete(e, func_name); });
timer_stack[timer_stack.length-1].start();
}
public static function delay_complete(e, func_name:Functio):void
{
e.target.stop();
e.target.removeEventListener(TimerEvent.TIMER, func_name);
timer_stack[timer_stack.length-1].removeEventListener(TimerEvent.TIMER_COMPLETE,
function(){ func_name_complete(e, func_name);} );
for(var i=0; i < timer_stack.length; i++)
{
if(timer_stack[i].running == true)
trace("timer # "+i+" is running");
if(timer_stack[i].running == false)
{
timer_stack[i] = null;
timer_stack.splice(i,1);
trace("remove timer # "+i);
}
}
}