I'm trying to get the bat character to remove the jack/2/3 child(s) in my game here, but I keep getting the ArgumentError: Error #2025. I understand that I may be removing a child twice, perhaps? I'm looking around and I'm not really experienced in this stuff so I'm having a hard time understanding what needs to be done to fix this issue. Can someone tell me what needs to be done with my code specifically, please?
var jack:pumpkin = new pumpkin();
var jack2:pumpkin = new pumpkin();
var jack3:pumpkin = new pumpkin();
var score:Number = 0;
scoreBox.text = String(score);
addChild(jack);
jack.x = 125;
jack.y = 285;
addChild(jack2);
jack2.x = 270;
jack2.y = 310;
addChild(jack3);
jack3.x = 445;
jack3.y = 285;
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveLeft);
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveRight);
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveDown);
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveUp);
stage.addEventListener(KeyboardEvent.KEY_UP, bump);
function moveLeft(e:KeyboardEvent):void {
if (e.keyCode == 37) {
bat.x -= 5;
}
}
function moveRight(e:KeyboardEvent):void {
if (e.keyCode == 39) {
bat.x += 5;
}
}
function moveDown(e:KeyboardEvent):void {
if (e.keyCode == 40) {
bat.y += 5;
}
}
function moveUp(e:KeyboardEvent):void {
if (e.keyCode == 38) {
bat.y -= 5;
}
}
function bump(e:KeyboardEvent):void {
stage.addEventListener(Event.ENTER_FRAME, bumpIt);
function bumpIt(e:Event):void {
if (bat.hitTestObject(jack)) {
stage.removeEventListener(Event.ENTER_FRAME, bumpIt);
removeChild(jack);
score++;
scoreBox.text = String(score);
}
if (bat.hitTestObject(jack2)) {
stage.removeEventListener(Event.ENTER_FRAME, bumpIt);
removeChild(jack2);
score++;
scoreBox.text = String(score);
}
if (bat.hitTestObject(jack3)) {
stage.removeEventListener(Event.ENTER_FRAME, bumpIt);
removeChild(jack3);
score++;
scoreBox.text = String(score);
}
}
}
Due to the nested bumpit() event handler, on every key up you're adding another enter frame event if your hit-test has no collision.
On KeyboardEvent.KEY_UP, event handler calls bump() which adds an Event.ENTER_FRAME listener that will call bumpIt().
If bat.hitTestObject() is true, you remove Event.ENTER_FRAME.
If not, you still have the Event.ENTER_FRAME listener firing bumpIt() every frame.
So, every key up you're potentially adding another frame handler.
If ten key up events occurred and none of them hit test your objects, you are now calling bumpIt ten times a frame.
If you need to hit test on key up, just put the logic there:
stage.addEventListener(KeyboardEvent.KEY_UP, bump);
function bump(e:KeyboardEvent):void {
if (bat.hitTestObject(jack)) { /* ... */ }
}
Or, if you're tracking an animation sequence after key up, maybe add some state variable, such as:
var isFlying:Boolean = false;
stage.addEventListener(KeyboardEvent.KEY_UP, bump);
function bump(e:KeyboardEvent):void {
// If bat is already flying, don't add another frame handler
if (isFlying)
return;
// Otherwise, indicate bat is now flying and add frame handler
isFlying = true;
stage.addEventListener(Event.ENTER_FRAME, bumpIt);
}
Then, if your hit-test works and you remove your frame handler, reset the state variable:
stage.removeEventListener(Event.ENTER_FRAME, bumpIt);
isFlying = false;
Another solution is to remove the nested handlers. Because you have nested bumpIt() inside bump(), you are seeing cumulative firing of callbacks due to scope:
function bump():void {
function bumpIt():void { /* ... */ }
}
Simply promote bumpIt():
function bump():void { /* ... */ }
function bumpIt():void { /* ... */ }
Related
I have some problems with my character's walking command. It delays its movement for a bit before it actually moves. And then at times it completely ignores the command to stop walking when I released the key.
Code:
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.MovieClip;
import flash.events.Event;
import flash.display.Stage;
hero.gotoAndStop(1);
var rightPressed: Boolean = new Boolean(false);
var leftPressed: Boolean = new Boolean(false);
var upPressed: Boolean = new Boolean(false);
var downPressed: Boolean = new Boolean(false)
var heroSpeed: Number = 10;
var keys: Array = [];
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
stage.addEventListener(Event.ENTER_FRAME, gameLoop);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
function keyDownHandler(KeyEvent: KeyboardEvent): void {
if (keys[Keyboard.RIGHT]) {
rightPressed = true;
} else if (keys[Keyboard.LEFT]) {
leftPressed = true;
} else if (keys[Keyboard.DOWN]) {
downPressed = true;
} else if (keys[Keyboard.UP]) {
upPressed = true;
}
}
function keyUpHandler(KeyEvent: KeyboardEvent): void {
if (keys[Keyboard.RIGHT]) {
rightPressed = false;
hero.gotoAndStop(4)
} else if (keys[Keyboard.LEFT]) {
leftPressed = false;
hero.gotoAndStop(2)
} else if (keys[Keyboard.DOWN]) {
downPressed = false;
hero.gotoAndStop(1);
} else if (keys[Keyboard.UP]) {
upPressed = false;
hero.gotoAndStop(3);
}
}
function gameLoop(loopEvent: Event): void {
if (rightPressed) {
hero.x += heroSpeed;
hero.gotoAndStop(8)
}
if (leftPressed) {
hero.x -= heroSpeed;
hero.gotoAndStop(6)
}
if (downPressed) {
hero.y += heroSpeed;
hero.gotoAndStop(5);
}
if (upPressed) {
hero.y -= heroSpeed;
hero.gotoAndStop(7);
}
}
function onKeyDown(e: KeyboardEvent): void {
keys[e.keyCode] = true;
}
function onKeyUp(e: KeyboardEvent): void {
keys[e.keyCode] = false;
}
Warnings:
Scene 1, Layer 'Actions', Frame 1, Line 68, Column 10 Warning: 1090: 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).
Scene 1, Layer 'Actions', Frame 1, Line 72, Column 10 Warning: 1090: Migration issue: The onKeyUp 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 ( 'keyUp', callback_handler).
You should get rid of the two extra KeyboardEvent handlers (onKeyUp and onKeyDown), and move the code you have there into keyUpHandler and keyDownHandler. That will solve those migration warnings (because onKeyUp and onKeyDown were special methods in AS2), and it may be the solution to your other problem: I guess sometimes the onKeyDown handler gets executed after keyDownHandler, which means the boolean values in your array are not set yet and no movement will start.
Even better: also get rid of the array with booleans (and keys! you're abusing an Array for Dictionary use) and do it like this:
function keyDownHandler(KeyEvent: KeyboardEvent): void {
if (event.keyCode==Keyboard.RIGHT]) {
rightPressed = true;
}
//etc
}
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.
I am totally new at Flash CS6 Action script 3. I have to do an assignment of a scene that have to have mouse, keyboard, enter frame, and time events. Every time I test the movie out, I keep on getting an error that says, 'duplicate function definition' about the 'enter frame event' and 'timer event' functions'. I have tried to rename the functions, but it didn't work. Is there another solution to this? Also, could you please show some examples? Thanks!
QuestionMC.addEventListener(MouseEvent.CLICK, onClick);
QuestionMC.addEventListener(MouseEvent.ROLL_OVER, questionOver);
QuestionMC.addEventListener(MouseEvent.ROLL_OUT, questionOut);
function questionOver(event:MouseEvent):void
{
event.target.alpha = .5;
}
function questionOut(event:MouseEvent):void
{
event.target.alpha = 1;
}
function onClick(event:MouseEvent):void
{
//trace("click!!!");
event.target.y -= 15;
event.target.rotation += 45;
}
QuestionMC.buttonMode = true;
stage.addEventListener(KeyboardEvent.KEY_DOWN, jump);
stage.addEventListener(KeyboardEvent.KEY_UP, land);
function jump (event:KeyboardEvent): void
{
trace(event.keyCode);
YoshiMC.y -= 50
stage.removeEventListener(KeyboardEvent.KEY_DOWN, jump);
stage.addEventListener(Event.ENTER_FRAME, flip);
}
function land (event:KeyboardEvent): void
{
YoshiMC.y += 50
stage.addEventListener(KeyboardEvent.KEY_DOWN, jump);
stage.removeEventListener(Event.ENTER_FRAME, flip);
}
function flip(event:Event):void
{
YoshiMC.rotation += 45;
YoshiMC.x += 20;
}
var jumpTimer:Timer = new Timer(5000,1);
jumpTimer.addEventListener(TimeEvent.TIMER, jump);
function jump (event:TimerEvent):void
{
planteaterMC.play();
}
jumpTimer.start();
var link:URLRequest = new URLRequest("");
buttongoeshere.addEventListener(MouseEvent.CLICK, clickHere);
function clickHere(event:MouseEvent):void
{
navigateToURL(link);
}
buttongoeshere.buttonMode = true;
Your jump function is declared twice, here:
function jump (event:KeyboardEvent): void
and here:
function jump (event:TimerEvent):void
You just need to rename one of them
Well the problem is that I'm having troubles with the fighter in my game. If I use the KEY_DOWN event and/or ENTER_FRAME, when I hold down the kick or punch button the fighter continuously causes damage to the enemy but I want him either to, for example kick and then return to still position or kick and be able to hold the position but only cause the damage one time. Here's some code:
stage.addEventListener(KeyboardEvent.KEY_DOWN, enterAttack);
stage.addEventListener(KeyboardEvent.KEY_UP, exitAttack);
stage.addEventListener(Event.ENTER_FRAME, attackYes);
stage.addEventListener(Event.EXIT_FRAME, attackNo);
function enterAttack(evt:KeyboardEvent):void
{
if (evt.keyCode == 84)
{
attack = true;
}
}
function exitAttack(evt:KeyboardEvent):void
{
attack = false;
}
function attackYes(evt:Event):void
{
if (attack)
{
hero.gotoAndStop("punch1");
checkHitRed();
checkIfDead();
}
}
function attackNo(evt:Event):void
{
if (!attack)
{
hero.gotoAndStop("still");
}
}
I was trying to remove the listener somewhere but that always made it look like the fighter didn't do any attack.
Is there any way to prevent holding down kick/punch button?
Any help is appreciated
You have to devise cooldowns for your abilities, e.g. "kick" can happen only once per 20 frames. So, when the player presses kick button, char does a kick, does damage, and sets the cooldown variable. Enter frame listener starts to decrement the counter, and subsequent kick commands are ignored while the cooldown is active.
stage.addEventListener(KeyboardEvent.KEY_DOWN, enterAttack);
stage.addEventListener(Event.ENTER_FRAME, enterFrame);
var kickCooldown:int=0;
function enterAttack(evt:KeyboardEvent):void
{
if (evt.keyCode == 84)
{
if (!kickCooldown) {
attack = true;
kickCooldown=15;
}
}
}
function enterFrame(evt:Event):void
{
if (attack)
{
hero.gotoAndStop("punch1");
attack=false; // we have attacked once, now dealing damage
checkHitRed();
checkIfDead();
}
if (kickCooldown>0) kickCooldown--; // waiting for cooldown end
else hero.gotoAndStop("still"); // if ends, return hero to "still" posture
}
Character state, divide into three types. As follows.
stage.addEventListener(KeyboardEvent.KEY_DOWN, enterAttack);
stage.addEventListener(KeyboardEvent.KEY_UP, exitAttack);
stage.addEventListener(Event.ENTER_FRAME, onAction);
var characterState:String = "wait"
function enterAttack(evt:KeyboardEvent):void
{
if (evt.keyCode == 84)
{
characterState = "attack";
}
}
function exitAttack(evt:KeyboardEvent):void
{
characterState = "attack_ended";
}
function onAction(evt:Event):void
{
if(characterState == "wait") return;
if(characterState == "attack")
{
hero.gotoAndStop("punch1");
checkHitRed();
checkIfDead();
}
else if(characterState == "attack_ended")
{
hero.gotoAndStop("still");
//Revert to wait state after the attack termination .
characterState = "wait";
}
}
I want to stop the movieclips movement when it hits a wall (another movieclip).
The example below works, but after the collision the movieclip 'blocks' all movement to the left...
My question to you is, is this a good way and why isn't it working well?
There will be something wrong in this code, but i'm learning.
For now the example with the leftArrow key;
variables to check the key, if it's hitting the walls and if it's moving or not:
var leftArrow:Boolean;
var speed:int = 10;
var hitting:Boolean;
var ismoving:Boolean;
event listeners for the keys/movement and detecting collision:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, keyReleased);
stage.addEventListener(Event.ENTER_FRAME, walking);
stage.addEventListener(Event.ENTER_FRAME, detectHit);
detecting collision function:
function detectHit(e:Event) :void
{
if(char.hitTestObject(bounds))
{
hitting = true;
}
}
function to the left arrow key:
function keyPressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT)
{
leftArrow = true;
}
}
function keyReleased(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT)
{
leftArrow = false;
}
}
And the reason it's not working is probably here, but I don't understand why not:
function walking(event:Event):void {
if (rightArrow) {
char.x += speed;
}
if (leftArrow && ! hitting) {
char.x -= speed;
}
else
{
ismoving = false
}
if (leftArrow && ! hitting)
char will move if hitting is false. When char.hitTestObject(bounds) is true you are setting hitting to true. You are not setting hitting again to false anywhere. That's why once left wall is hit it stops left movement permanently. You need to figure out suitable condition to set hitting to false again.
Adding an else branch in detectHit should solve the problem.
function detectHit(e:Event):void {
if(char.hitTestObject(bounds))
{
hitting = true;
} else {
hitting = false; // add this
}
}
Allthough Taskinoor's method should work, I would suggest another way to do your hittests.
Since you probably are creating a game (character and bounds), you will have more than one bound. In that case, I would strongly suggest bitmap-hittesting. This way, you can create all your bounds in one movieclip and test for a hit.
I will explain this by using the example of a maze. The maze would then be some lines in a movieclip, randomly put together. If you use HitTestObject and you aren't hitting one of the lines, but your character is over the movieclip, hitTestObject will return true, even though you are not hitting a wall. By using bitmapHitTesting, you can overcome this problem (BitmapHitTest takes transparant pixels into account, whereas hitTestObject does not).
Below you can find an example of how to do bitmapHitTesting. Creating the bitmaps in this function is not necesarry if they do not change shape. In that case, I would suggest placing the code for the bitmapdata in a added_to_stage-method.
private var _previousX:int;
private var _previousY:int;
private var _bmpd:BitmapData ;
private var _physicalBitmapData:BitmapData;
private function walkAround(e:Event):void
{
var _xTo:int = //Calculate x-to-position;
var _yTo:int = //Calculate y-to-position;
//If your character doesn't change shape, you don't have to recalculate this bitmapData over and over.
//I suggest placing it in a ADDED_TO_STAGE-Event in that case.
_bmpd = new BitmapData(char.width, char.height, true, 0);
_bmpd.draw(char);
//If your bounds are static, you don't have to recalculate this bitmapData over and over.
//I suggest placing it in a ADDED_TO_STAGE-Event in that case.
_physicalBitmapData = new BitmapData(bounds.width, bounds.height, true, 0);
_bmpd.draw(bounds);
//The below line is the actual hittest
if(_physicalBitmapData.hitTest(new Point(0, 0), 255, _bmpd, new Point(char.x, char.y), 255))
{
char.x = _previousX;
char.y = _previousY;
}
else
{
char.x = _xTo;
char.y = _yTo;
}
_previousX = char.x;
_previousY = char.y;
}
Look at my hint,
function loop(Event)
{
if(isHit==false)
{
if(isRight==true){head_mc.x+=1}
if(isLeft==true){head_mc.x-=1}
if(isUp==true){head_mc.y-=1}
if(isDown==true){head_mc.y+=1}
}
if(head_mc.hitTestObject(build_mc))
{
isHit=true;
if(isRight==true){head_mc.x-=1}
if(isLeft==true){head_mc.x+=1}
if(isUp==true){head_mc.y+=1}
if(isDown==true){head_mc.y-=1}
}
else
{
isHit=false;
}
}
I use step back to opposite direction instead.