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

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;
}
}

Related

I am having trouble with hit detection

I am having trouble with getting my hit detection to work in as3. I am making a flappy bird like game and when the player dies and goes back to frame 2, we get error #1009. I am new to this so if you could make this as simple as possible it would be much appreciated.Thank you! Here is my code.
import flash.events.Event;
stage.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void
{
player.y += -100;
}
var randomX:Number = Math.random() * 550;
player.x = 50;
player.y = 50;
var speed:Number = 5;
player.addEventListener(Event.ENTER_FRAME, moveDown);
function moveDown(e:Event):void {
e.target.y += speed;
if(e.target.y >= 400) {
{
gotoAndStop(2);
player.removeEventListener(Event.ENTER_FRAME, moveDown);
}
}
}
var gravity = 8;
stage.addEventListener(Event.ENTER_FRAME, movewt);
function movewt(e:Event):void
{
wt.x = wt.x - 5;
}
stage.addEventListener( Event.ENTER_FRAME, handleCollision)
function handleCollision( e:Event ):void
{
if(player.hitTestObject(wt))
{
gotoAndStop(2);
stage.removeEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
player.removeEventListener(Event.ENTER_FRAME, moveDown);
stage.removeEventListener(Event.ENTER_FRAME, movewt);
stage.removeEventListener( Event.ENTER_FRAME, handleCollision);
}
}
Problem
When this code goes to frame 2
if(e.target.y >= 400)
{
{
gotoAndStop(2);
player.removeEventListener(Event.ENTER_FRAME, moveDown);
}
}
it only removes the ENTER_FRAME event listener from player, all other handlers persist, namely:
fl_MouseClickHandler
movewt
handleCollision
The latter two are executed on their own, which makes them the problematic ones, because the objects that they access are not necessarily present on frame 2.
movewt accesses wt, handleCollision accesses both player and wt. If either player or wt does not exist on frame 2, they are null.
Solution
Don't use frames! While it appears to be very easy to build navigations based on frames, you quickly run into problems like the one at hand. Frames were made for animations and they are great at doing that, so use them for that. How to not use frames is beyond the scope of this answer, there are many resources on the web on how to do that. Most game frameworks don't even have frames and use state machines for that purpose. It's highly recommended to take a look at how game frameworks handle this.
Another problem is the inconsistent way you switch between frames. Sometimes you remove one event listener, sometimes you remove some others...did you just forget to remove all of them? Why do you have this duplicated logic in the first place? If you have different states of your game (say for example, the game itself, which is currently on frame 1 (or whatever frame the code you posted is on) and some game over screen at frame 2), then you should have a clearly defined way to transition between the two states.
If you want to go to the game over state, have one function that does this and do all the cleanup in that function:
function gameOver():void
{
gotoAndStop(2);
stage.removeEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
player.removeEventListener(Event.ENTER_FRAME, moveDown);
stage.removeEventListener(Event.ENTER_FRAME, movewt);
stage.removeEventListener( Event.ENTER_FRAME, handleCollision);
}
Now, whenever you want to change to that state, be it because if the end of the level or because of a collision, call that function:
import flash.events.Event;
var speed:Number = 5;
var gravity = 8;
var randomX:Number = Math.random() * 550;
player.x = 50;
player.y = 50;
stage.addEventListener(Event.ENTER_FRAME, movewt);
stage.addEventListener( Event.ENTER_FRAME, handleCollision);
player.addEventListener(Event.ENTER_FRAME, moveDown);
stage.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void
{
player.y += -100;
}
function moveDown(e:Event):void
{
e.target.y += speed;
if(e.target.y >= 400)
{
gameOver();
}
}
function movewt(e:Event):void
{
wt.x = wt.x - 5;
}
function handleCollision(e:Event):void
{
if(player.hitTestObject(wt))
{
gameOver();
}
}
function gameOver():void
{
gotoAndStop(2);
stage.removeEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
player.removeEventListener(Event.ENTER_FRAME, moveDown);
stage.removeEventListener(Event.ENTER_FRAME, movewt);
stage.removeEventListener( Event.ENTER_FRAME, handleCollision);
}
Again, ideally you'd use a framework that provides classes that represent game states. This allows for clearly defined transitions between states:
As the code stands now, you have to remember that gameOver() should be called in order to make a proper transition to the game over state and you could still do it in another way. With a library, there's no "other way", only one. But for now, gameOver() will do and should solve your problem.

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.

Problems getting keyboard input in AS3

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.

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
}

AS3 Works but I get a ReferenceError: Error #1069 Property startDrag not found

I am trying to make a simple project when you click a button a draggable MovieClip is added to the stag and when you click it releases the MovieClip to the X/Y where you clicked, you can then pickup the MovieClip and drag it into a bin (MovieClip) where it destroys itself. The code is working great I can make multiple Movieclips with the button and they are all destroyed when I drag them in the bin however I don't like having "Error Codes".
import flash.events.MouseEvent;
var rubbish:my_mc = new my_mc();
btntest.addEventListener(MouseEvent.CLICK, makeRubbish);
function makeRubbish (event:MouseEvent):void {
addChild(rubbish);
rubbish.x = mouseX - 10;
rubbish.y = mouseY - 10;
rubbish.width = 50;
this.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
rubbish.buttonMode = true;
}
function stopDragging (event:MouseEvent):void {
rubbish.stopDrag()
event.target.addEventListener(MouseEvent.CLICK, startDragging);
rubbish.buttonMode = true;
if (event.target.hitTestObject(bin))
{
trace("hit");
event.target.name = "rubbish";
removeChild(getChildByName("rubbish"));
}
}
function startDragging (event:MouseEvent):void {
event.target.startDrag();
this.addEventListener(MouseEvent.CLICK, stopDragging);
}
Some Pointers:
The target property of an Event is not always what it seems. It actually refers to the current phase in the event bubbling process. Try using the currentTarget property.
I would also recommend tying the stopDragging method to the stage, as sometimes your mouse won't be over the drag as you're clicking.
I would use the MOUSE_UP event as opposed to a CLICK for standard dragging behaviour.
When dragging, keep a global reference to the drag in order to call the stopDrag method on the correct object.
Try This:
import flash.events.MouseEvent;
var rubbish:my_mc = new my_mc();
var dragging:my_mc;
btntest.addEventListener(MouseEvent.CLICK, makeRubbish);
function makeRubbish (event:MouseEvent):void {
addChild(rubbish);
rubbish.x = mouseX - 10;
rubbish.y = mouseY - 10;
rubbish.width = 50;
rubbish.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
rubbish.buttonMode = true;
}
function stopDragging (event:MouseEvent):void {
this.stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
if(dragging !== null){
dragging.stopDrag();
if (event.currentTarget.hitTestObject(bin)){
removeChild(dragging);
}
dragging = null;
}
}
function startDragging (event:MouseEvent):void {
dragging = event.currentTarget as my_mc;
dragging.startDrag();
this.stage.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}