Problems getting keyboard input in AS3 - actionscript-3

So I'm workin on a flash project where I want keyboard input. In the stage there's an instance "Car" seen from above which is supposed to be rotate and drive direction of rotation. This is what I've put together so far in AS3:
//Required stuff
import flash.ui.Keyboard;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.display.Stage;
import flash.display.MovieClip;
Car.addEventListener(Event.ENTER_FRAME, this.RunGame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
//Variables
var keys:Array = []
var vDrive:Number = 3; //Car's current base speed
var vx:Number = 0; //Speed along x axis
var vy:Number = 0; //Speed along y axis
var vMax:Number = 30; //Top speed
var vRot:Number = 3; //Rotation speed
var vAcc:Number = 1.1; //Factor for acceleration
var vDeAcc:Number = 0.90; //Factor for de-acceleration
//Game Loop
RunGame();
function RunGame():void
{
// Drive forwards
if (keys[Keyboard.UP])
{
if (vDrive < vMax)
vDrive += vAcc;
}
// Reverse
if (keys[Keyboard.DOWN])
{
if (vDrive > vMax)
vDrive *= vAcc;
}
// Turn right
if (keys[Keyboard.RIGHT])
{
Car.rotation += vRot;
}
// Turn left venstre
if (keys[Keyboard.LEFT])
{
Car.rotation -= vRot;
}
//Movement
// Friction
vDrive *= vDeAcc;
//Calculating movement vector
vx = vDrive * Math.cos(toRad(Car.rotation));
vy = vDrive * Math.sin(toRad(Car.rotation));
//Update car position
Car.x -= vx ;
Car.y -= vy;
}
However, when I run the program, the arrow keys don't seem to do anything.
I also get the following compiler warnings for both "onKeyDown" and "onKeyUp":
Migration issue: The onKeyDown event handler is not triggered
automatically by Flash Player at run time in ActionScript 3.0. You
must first register this handler for the event using addEventListener
( 'keyDown', callback_handler)
Trying to add what it suggested just makes errors saying callback_handler ain't defined.
I'm now stuck trying to figure out how to make the keyboard input work. Anyone know?

You are currently missing the functions for the key listeners. You have added the listeners to the stage here:
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
Now you just need to create the functions:
function onKeyDown( e:KeyboardEvent ):void {
//add our key to the keys array
keys[e.keyCode] = e.keyCode;
}
function onKeyUp( e:KeyboardEvent ):void {
//if the key is in the keys array, set the value to null
keys[e.keyCode] = null;
}
But there is another problem here:
Car.addEventListener(Event.ENTER_FRAME, this.RunGame);
You do not need the this.RunGame, just RunGame will do, but you should get an error this method needs a parameter of type Event, so your RunGame() definition should look like this:
function RunGame(e:Event):void
Then you wouldn't call RunGame(), it is called each frame while tied to the event listener.
Edit: Please note that there are many ways to handle key events, my answer will work with your current implementation.

Related

stop() caller not working properly

I am making a brick breaker game with three frames. The first frame is the start screen, the second frame is the game itself, and the third frame is the "game over" screen (with a try again button). When I hit "Start game" the program jumps to the second frame and stops. If you fail to hit the ball with the racket, the program jumps to frame three.
My problem occurs here, because the program instantly jumps to the second frame again. Any idea why the stop(); caller fails to work? I have tried to remove all content from the last frame (except for the stop(); caller), but it still just skips back to frame 2.
I really can't figure out why this is happening. I am using Adobe Flash Professional CC. The only actionscript on frame 3 are "stop();". This is the entire code block on frame 2:
import flash.events.KeyboardEvent;
import flash.display.Stage;
import flash.events.Event;
import flash.ui.Keyboard;
import fl.transitions.Tween;
import fl.transitions.easing.*;
trace(currentFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
this.addEventListener(Event.ENTER_FRAME, moveBall);
var rackert: bar = new bar();
rackert.name = "rackert";
rackert.y = 740;
rackert.x = 640;
addChild(rackert);
var ball: circle = new circle();
ball.y = 80;
ball.x = 640;
addChild(ball);
var ballXSpeed: Number = 12; //X Speed of the Ball
var ballYSpeed: Number = 12; //Y Speed of the Ball
function keyDown(e: KeyboardEvent) {
var key: uint = e.keyCode;
var step: uint = 35;
switch (key) {
case Keyboard.LEFT:
if (rackert.x > 0) {
var myTween: Tween = new Tween(rackert, "x", Regular.easeOut, rackert.x, rackert.x - step, 0.2, true);
} else rackert.x = 0;
break;
case Keyboard.RIGHT:
if (rackert.x + rackert.width < 1000) {
var myTween2: Tween = new Tween(rackert, "x", Regular.easeOut, rackert.x, rackert.x + step, 0.2, true);
} else rackert.x = 1000 - rackert.width;
break;
}
}
var gameOver: Boolean = false;
function moveBall(event: Event): void {
ball.x += ballXSpeed;
ball.y += ballYSpeed;
if (ball.x >= 1000 - (ball.width / 2)) {
ballXSpeed *= -1;
}
if (ball.x <= 0 + (ball.width / 2)) {
ballXSpeed *= -1;
}
if (ball.y >= stage.stageHeight) {
if (gameOver == false) {
gotoAndStop(3);
this.removeEventListener(Event.ENTER_FRAME, moveBall);
stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDown);
gameOver = true;
rackert.visible = false;
}
}
if (ball.y <= 22) {
ballYSpeed *= -1;
}
if (ball.hitTestObject(rackert)) {
calcBallAngle();
}
}
function calcBallAngle(): void {
var ballPosition: Number = ball.x - rackert.x;
trace("Position: " + ballPosition);
var hitPercent: Number = (ballPosition / (rackert.width - ball.width)) - .7;
trace("percent: " + hitPercent);
ballXSpeed = hitPercent * 10;
ballYSpeed *= -1;
}
function getRandom(min: Number, max: Number): Number {
return min + (Math.random() * (max - min));
}
Change this:
if (gameOver == false) {
gotoAndPlay(3); //gotoAndPlay(); caller
gameOver = true;
rackert.visible = false;
}
To:
if (gameOver == false) {
gotoAndStop(3); //gotoAndPlay(); caller
gameOver = true;
rackert.visible = false;
}
Difference is goToAndStop(). The default behavior is to "loop" an animation, so you tell it to go to frame 3 (last frame) and it "plays" through that frame back around to 1, then 2, where you most likely have a frame script that calls stop(); to stop the play head.
Update
I believe you that you're calling stop(); in frame 3. It seems like it should work and indeed it actually is, it's just not working on the object that you're expecting it to work on. Since you're using a frame script, stop(); is being called on the InteractiveObject who's scope the frame script is inside of. Let me clarify.
Frame 3 Of Stage
-> Child on frame three called FrameScriptsArePITA
-> Double click FrameScriptsArePITA and write a frame script "stop()", the script will do nothing but stop FrameScriptsArePITA from playing.
Watch your scope. That's part of why frame scripts are... best to avoid. Using your own DocumentClass and hooking everything in your design view into corresponding classes will make things easier to solve in AS3.
I finally found the issue. I had a timer event on frame 1, which caused the bug. I simply used removeEventListener for the timer function where i skip to frame 2. As Technick Empire said, you should always be cleaning up anything including even listeners as they can even interfere with the garbage collector and cause memory leaks.

AS3 removing event listener before child still causing null object reference

I made a game of Pong with external classes, it's currently configured so that when the playerScore/cpuScore == 1, the player, CPU and ball is removed from the stage. These are removed by the main.as file calling removePlayer(), removeBall() and removeCPU() e.g.
public function removePlayer(): void
{
removeEventListener(Event.ENTER_FRAME, loop);
parent.removeChild(this);
}
The event listener gets rid of the main loop for the element before the child is removed from stage. This works fine for both the player and CPU, but for the ball I'm getting the following error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.classes::ball/loop()
Commenting out parent.removeChild(this); gets rid of the error, but obviously the ball stays visible. removeEventListener seems to have worked as the ball stops moving when called, but I'm at a loss as to why I would get the null object reference if the loop has been removed. Here is the code for my ball:
package com.classes
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
public class ball extends MovieClip
{
private var theBackground:bg;
private var bounce:Sound = new Sound();
private var point:Sound = new Sound();
private var channel:SoundChannel = new SoundChannel();
public var ballSpeedX:int = -3;
public var ballSpeedY:int = -2;
public function ball(score:bg)
{
theBackground = score;
bounce.load(new URLRequest("com/sounds/sfxBounce.mp3"));
point.load(new URLRequest("com/sounds/sfxScore.mp3"));
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e:Event)
{
x += ballSpeedX;
y += ballSpeedY;
if (x <= width/2)
{
x = width/2;
ballSpeedX *= -1;
theBackground.cpuScore++;
theBackground.updateScores();
channel = point.play();
}
else if (x >= stage.stageWidth - width/2)
{
x = stage.stageWidth - width/2;
ballSpeedX *= -1;
theBackground.playerScore++;
theBackground.updateScores();
channel = point.play();
}
if (y <= height/2)
{
y = height/2;
ballSpeedY *= -1;
channel = bounce.play();
}
else if (y >= stage.stageHeight - height/2)
{
y = stage.stageHeight - height/2;
ballSpeedY *= -1;
channel = bounce.play();
}
}
public function removeBall(): void
{
removeEventListener(Event.ENTER_FRAME, loop);
parent.removeChild(this);
}
}
}
Any help or pointers would be greatly appreciated!
You can add a if (!stage) return; statement in the beginning of the loop function. Apparently you are trying to call removeBall() while processing another Event.ENTER_FRAME listener, maybe on the clock or the player or elsewhere, this hits an issue with event's listener sequence already cached inside Flash. That is, if you are removing an event listener for a certain event type from within another listener of the very same event (enter frame event qualifies), the listener still gets called for that event. You should either call removeBall() from elsewhere, not from an enterframe listener, or just have a null check in that listener to avoid weird errors popping up.
EDIT: Apparently your removeBall() is called while you do theBackground.updateScores(), and after you do this, the ball that processes the event is already removed, thus further processing of the listener is useless. Place a return statement after your sound is triggered to play in all clauses that call theBackground.updateScores() and you should be fine.
if (x <= width/2)
{
x = width/2;
ballSpeedX *= -1;
theBackground.cpuScore++;
theBackground.updateScores();
point.play();
return; // here
}
else if (x >= stage.stageWidth - width/2)
{
x = stage.stageWidth - width/2;
ballSpeedX *= -1;
theBackground.playerScore++;
theBackground.updateScores();
point.play();
return; // and here
}

Why are my array movie clips getting harder to click when they get faster?

I am making a game where insects come down from the top of the screen, and the user must kill them. The insects are in an array. Each time the user kills them, the score goes up..after a while the insects get faster and faster. When they get faster, some of them don't get killed when you click them. You have to click multiple times for them to die. I want them to get killed in one click, but this isn't working when they get faster!
function makeEnemies():void
{
var chance:Number = Math.floor(Math.random() * 150);
if (chance <= + level)
{
//Make sure a Library item linkage is set to Enemy...
tempEnemy = new Enemy();
//Math.random(); gets a random number from 0.0-1.0
tempEnemy.x = Math.round(Math.random() * 1000);
addChild(tempEnemy);
enemies.push(tempEnemy);
tempEnemy.speed = enemyBaseSpeed + ((level - 1) * speedLevelInc);
}
}
function moveEnemies():void
{
var tempEnemy:MovieClip;
for (var i:int =enemies.length-1; i>=0; i--)
{
tempEnemy=enemies[i];
if (tempEnemy.dead)
{
score++;
score++;
roachLevel.score_txt.text = String(score);
enemies.splice(i,1);
}
else // Enemy is still alive and moving across the screen
{
//rotate the enemy between 10-5 degrees
tempEnemy.rotation += (Math.round(Math.random()*.4));
//Find the rotation and move the x position that direction
tempEnemy.x -= (Math.sin((Math.PI/180)*tempEnemy.rotation))*tempEnemy.speed;
tempEnemy.y += (Math.cos((Math.PI/180)*tempEnemy.rotation))*tempEnemy.speed;
if (tempEnemy.x < 10)
{
tempEnemy.x = 11;
}
if (tempEnemy.x > stage.stageWidth - offset)
{
tempEnemy.x = stage.stageWidth - offset;
}
if (tempEnemy.y > stage.stageHeight)
{
removeEnemy(i);
lives--;
roachLevel.lives_txt.text = String(lives);
}
}
}
}
function removeEnemy(id:int)
{
removeChild(enemies[id]);
enemies.splice(id,1);
}
There is also code inside the insect.
import flash.events.MouseEvent;
import flash.display.MovieClip;
import fl.motion.Animator;
import flash.events.*;
play();
var MainTimeLine = MovieClip(root);
var mysound:squish = new squish();
this.addEventListener(MouseEvent.CLICK, kill);
this.dead = false;
function kill(e:MouseEvent):void
{
this.dead=true;
mouseChildren=false
mysound.play();
gotoAndPlay(21);
this.removeEventListener(MouseEvent.CLICK, kill);
flash.utils.setTimeout(removeSelf,2000);
}
function removeSelf():void
{
this.parent.removeChild(this);
}
You shouldn't remove an enermy from the array while iterating it.
You make enemies.splice(i,1); in your loop iterating from enemies.length to 0. While you changing your array size, you don't adjust the loop condition.
I think your main issue might be that you are reusing your insects, maybe pooling them. If you do this, you need to make sure that you are adding the eventListener for the click again when recycling.
If you are adding the listener in the constructor, that will only execute when the insect is created, not when you recycle him.
Your issue is the setTimeOut(), which is causing a memory leak. Using a timer is much safer, but if you must use it, keep a reference to the call and clear it when you no longer need it.
Also, the code you posted doesn't show where you're adding a listener to MainTimeline or parent, but if you are you need to remove that as well before the insect can be garbage collected.

How to remove a function or event stop it from stage.addEventListener in as3

I´m trying to make object follow the mouse in as3.
My wish is when I roll over a movieclip(btn1) I want the function that make object follow the mouse(my_object) stop until I roll out of it.
HERE IS THE SCRIPT:
btn1.addEventListener(MouseEvent.ROLL_OVER, JD);
function JD(event:MouseEvent):void{
stage.removeEventListener(Event.ENTER_FRAME, follow_me);
}
btn1.addEventListener(MouseEvent.ROLL_OUT, kk);
function kk(event:MouseEvent):void{
play();
}
stage.addEventListener(Event.ENTER_FRAME,follow_me)
function follow_me(event:Event):void {
var dx:int = bracketL.x - mouseX;
var dy:int = bracketL.y - mouseY;
my_object.x -= dx / 9+5;
my_object.y -= dy /9;
}
Even I roll over the btn1, the my_object does not stop, it still follow the mouse !!
WHAT SHOULD I DO ?
Using a different approach for smoother animation, you could create a enter frame handler that checks for a paused state variable.
Each frame, your object is animated following the mouse cursor; however, if the mouse rolls over your button the object is paused from tracking the mouse.
Example SWF
CS6 FLA source code
CS5 FLA source code
Code:
import flash.events.Event;
import flash.events.MouseEvent;
var paused:Boolean = false;
addEventListener(Event.ENTER_FRAME, frameHandler);
button.addEventListener(MouseEvent.ROLL_OVER, buttonOverHandler);
button.addEventListener(MouseEvent.ROLL_OUT, buttonOutHandler);
function buttonOverHandler(event:MouseEvent):void
{
paused = true;
}
function buttonOutHandler(event:MouseEvent):void
{
paused = false;
}
function frameHandler(event:Event):void
{
if (!paused)
{
object.x -= (object.x - mouseX) * 0.1;
object.y -= (object.y - mouseY) * 0.1;
}
}

AS3 - If symbol's coordinates arrive here?

I'm using Flash Professional CS5.5 and I need to make an app where there is a ball (symbol) that moves using the accelerometer and I want that, when the ball coordinates A reach this coordinates B it goes to frame 2 (gotoAndPlay(2)). I have to find the ball coord first, right? How do I make this?
Here is the code I've now
c_ball.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void{
c_ball.startDrag();}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void{
c_ball.stopDrag();}
would it work if, after retriving the coordinates?
function f_level (e) if (c_ball.x==100 && c_ball.y==100) {
gotoAndStop(2);}
MOUSE_UP and MOUSE_DOWN are not what you need if you're looking for Accelerometer data. You want the Accelerometer class and associated events.
Try something like this:
import flash.sensors.Accelerometer;
import flash.events.AccelerometerEvent;
var accel:Accelerometer = new Accelerometer();
accel.addEventListener(AccelerometerEvent.UPDATE, handleAccelUpdate);
Update handler:
function handleAccelUpdate(e:AccelerometerEvent):void{
//inside this function you now have access to acceleration x/y/z data
trace("x: " + e.accelerationX);
trace("y: " + e.accelerationY);
trace("z: " + e.accelerationZ);
//using this you can move your MC in the correct direction
c_ball.x -= (e.accelerationX * 10); //using 10 as a speed multiplier, play around with this number for different rates of speed
c_ball.y += (e.accelerationY * 10); //same idea here but note the += instead of -=
//you can now check the x/y of your c_ball mc
if(c_ball.x == 100 && c_ball.y == 100){
trace("you win!"); //fires when c_ball is at 100, 100
}
}
Now this will let you "roll" your MC off the screen so you're probably going to want to add some kind of bounds checking.
Check out this great writeup for more info:
http://www.republicofcode.com/tutorials/flash/as3accelerometer/
An easy and save way is to use colission detection, instead of testing for exectly one position ( what is hard to meet for users) you go for a target area :
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Hittester extends Sprite
{
var ball:Sprite = new Sprite();
var testarea:Sprite = new Sprite();
public function Hittester()
{
super();
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(0,0,10);
testarea.graphics.beginFill(0x00ff00);
testarea.graphics.drawRect(0,0,50,50);
testarea.x = 100;
testarea.y = 100;
// if testarea should be invisble
/*testarea.alpha = 0;
testarea.mouseEnabled = false;
*/
ball.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
addChild(testarea);
addChild(ball);
}
private function startDragging( E:Event = null):void{
ball.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
private function stopDragging( E:Event = null):void{
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
ball.stopDrag();
test();
}
private function test():void{
if( ! ball.hitTestObject(testarea) ){
ball.x = 10;
ball.y = 10;
}
else{
// here goes next frame command ;)
}
}
}
}