Keyboard listener is killing my framerate - actionscript-3

I'm working on a game that uses four simultaneous key presses. It all works fine, except that when the keys are rapidly pressed, my framerate slows down significantly (if I hammer even just one of the keys, I can halve the framerate).
I initially just assumed that there was too much going on in the method that the key press triggers, but if I take the code out of the method completely, the slowdown still occurs.
Has anyone run into this before? The keypress is one where you hold the key down, so it's repeatedly firing a method call every frame, but this is pretty standard for many uses and I've never encountered this before.
EDIT: clarification.
Structurally, there's a KEY_DOWN and KEY_UP listener attached to the stage:
stage.addEventListener(KeyboardEvent.KEY_DOWN, menuKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, menuKeyUp);
which then calls a method with a single switch statement in, that contains five lines like this:
case ONE: pressing1 = true; break;
There's an ENTER_FRAME event that checks to see if any of the boolean flags are true, and handles character movement. This calculation happens regardless of key presses (i.e. if you let go of the keys, gravity still has an effect). This holds 60fps easily.
The issue is literally at the exact moment the key is pressed or released, there's an almost imperceptible frame drop. Repeatedly pressing the keys causes the framedrop to get worse and worse. Holding a key down doesn't kill the framerate, apart from the single split second frame drop when the key is pressed down. After that the game carries on as normal. Letting go of the key causes another tiny hitch and then the framerate goes back to normal.
EDIT 2 - I added a framerate checker so I could see exactly what was happening to the framerate. Interestingly, I can't make it go any lower than EXACTLY 30fps even when I press keys very rapidly. Is there some sort of restriction in play here with Flash Player?

Turns out that the code was correct all along. Playing 60FPS content in the debug player or the standalone player causes any events (mouse and keyboard) to hitch the framerate. Viewing the same content in a browser, or exported to AIR, stops the issue entirely. The content is now running perfectly at 60fps without any slowdown.
Hope this helps someone, I was tearing my hair out!

Do you have multiple different listeners, or one listener that appropriately routes the keypress? I would suspect the former. The solution is to switch to the latter.
What I typically do is have one object that's responsible for listening to key presses and translating keyboard events into other, more meaningful events.
For example:
protected function handleKeyboardEvent(e:KeyboardEvent):void {
if (e.ctrlKey) {
switch (e.keyCode) {
case Keyboard.A:
eventBus.dispatchEvent(new Event(ViewEventKind.SELECT_ALL));
return;
case Keyboard.Y:
eventBus.dispatchEvent(new Event(ModelEventKind.REDO));
return;
case Keyboard.Z:
eventBus.dispatchEvent(new Event(ModelEventKind.UNDO));
return;
}
}
}

Related

Flash CS3 null reference to button/mc after returning to Frame

I've been researching this issue for hours and while I've found somewhat similiar situations, i have yet to find a simple fix. Basically I have a timeline where some animation plays. Eventually I get to my main game screen (frame 256) where a stop(); is called and the user then gets to click on one of 3 doors. Clicking on any door takes the user ahead a bunch of frames and they get to play a game. Once the game is done or the user clicks back, it takes the user back to the original frame (frame 256) and every single time it does this, it says my "upDoor" is a null reference and then the upDoor button(instance of upStairsDoor button) is no longer on the stage.
This seems to happen regardless of which door the user picks first. If the user picks the upDoor and plays the minigame there or picks the outsideDoor and plays that specific minigame, when the user returns to this frame (frame 256), it throws this error on the door and then of course because it throws an error, nothing else works at that point and I have to exit the game.
It's not a typo! Please don't suggest I check my instance names. As I mentioned, the door works fine the first time you get to the frame, it's just when you go back to it. I've read that it might have to do with the garbage collector but when we return to the frame, shouldn't it recreate all of the instances that I've placed to the stage? It doesn't error on ANY other button or movieClip that I've dragged to the stage, only this one particular door.
I forgot to mention that it's error'ing on a line of code that references the upDoor button. I have these lines of code here...(frame 256)
if (downDoor.enabled) {
downDoor.enabled = false;
}
if (upDoor.enabled) {
upDoor.enabled = false;
}
if (outDoor.enabled) {
outDoor.enabled = false;
}
What these do is disable the doors until the user clicks another object on the screen which then runs a function that sets all the doors to enabled. The error in question is saying I can't access a property of a null reference.
Thanks for the input guys. What ended up being the solution for me was to implement all of the movieclips and buttons programatically when the frame loads and then remove them all when I switch frames. That way, everytime the frame is reloaded from a gotoAndPlay, everything gets re-created again.

AS3 key presses cause animation error

I'm making a game and basically I'm having some errors with the animation state.
public function movementChar()
{
if (touchingGround)
{
if (rightKey)
{
gotoAndStop("run");
scaleX = 1;
}
if (leftKey)
{
gotoAndStop("run");
scaleX = -1;
}
if (upKey)
{
gotoAndStop("jump");
this.y -= 15;
//touchingGround = false;
}
if (attackKey)
{
gotoAndStop("attack");
}
if (!rightKey && !leftKey && !upKey && !attackKey)
{
gotoAndStop("stop");
}
}
}
I have some other coding which says if the player is touching the ground then touchingGround = true;
and if it is true then the player can move right, left, jump and attack.
The problem is that when I press the attack key, it keeps on looping the animation and attacking.
I want the attack key to play the animation once and make the boolean hasAttacked = true; once.
Another problem is that when the player is moving and the attack key has been pressed/ hold down the animation freezes. Flash gets confused on which animation to play so it stops at frame 1 and glitches.
I would appreciate it if someone can give me an idea on how to fix this.
Thank you.
Ok I have a few recommendations. First let's address the fact that your attack animation is looping. You will need to go into the SirTimmyAttack movieclip timeline and add
stop();
In to frame 4 to keep the animation from looping forever. After you do that the animation will play once. You have a timer that you are starting when you tell him to attack, after the timer execute he will attack again. So if you hold the "A" key he will ATTACK...Wait for timer...ATTACK...Wait for timer....ATT ect. if you want him only to attack once per press of the "A" key remove that timer and never start it.
On to address your second concern about the player being unable to attack and walk at the same time. That is a technical limitation of the way you are currently animating. Since the players entire animation is contained inside a set of frames you cant mix and match to combine them. I would suggest breaking your player graphics/animations into to halves. An Upper body, and a lower body. That way you can trigger walking/running/standing animations independently from the upperbody/attack animations.
Hope this helps, let me know if you have any other questions.
I don't have my flash dev stuffs on my current machine so I can't open the fla but:
I suspect that this is caused by not 'consuming' the key event. i.e. presumably you are setting rightKey, leftKey, attackKey etc on key up and key down events and checking the state during the update function?
The problem with this is that each update it will act as if pressing the key the first time, every time, you need some method of knowing that the key press has been handled.
As I say I can't check your fla so I can't comment on the best method but I suspect you may have to substantially change your input handling.

AS3 - KeyboardEvent.KEY_DOWN in AIR project not triggering handler after removing display objects, and stage.focus not working

I am developing an kiosk-like application (a game) which needs to be locked in full screen all the time. I am using as3/flash/AIR for it. Things started well at first, and for the most part all works fine.. but there is a mystery brewing somewhere which I haven't been able to figure out... That's where your help would be greatly appreciated!
The way I handled this problem is by adding at the very beginning of the app:
stage.addEventListener(KeyboardEvent.KEY_DOWN, playerOnKeyDown);
Then, on my playerOnKeyDown function:
function playerOnKeyDown(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.ESCAPE)
{
event.preventDefault();
//More code here opening out menus, etc, etc.)
}
}
So, all of this worked just fine, but of course I needed to also bring along:
stage.focus = stage;
into the party, otherwise, when removing objects - as in removeChild() - the event firing wouldn't behave as I wanted, because flash changed the focus elsewhere in the display list.
I have been careful to add the focus to the stage every time a remove a "child", and it works great everywhere, except for one time in the entire run, right after I remove an object from an externally loaded swf.
I still add the lines as it should be expected to work:
removeChild(childFromLoadedSWF);
stage.focus = stage;
except that when I hit the any key, the event won't trigger my function, and if I hit the ESC key, it takes me out of full screen (its default behavior), once again, circumventing completely my listener function playerOnKeyDown.
The strange thing is that right before doing this, the line:
stage.hasEventListener(KeyboardEvent.KEY_DOWN))
traces true!
The focus is on the stage, the listener is on, and yet when pressing the ESC key the default behavior is ignoring my function completely....
What could be causing this?
THANK YOU!!
Looks like I just needed to remove an ENTER_FRAME listener I had. That was wreaking havoc with the stage.focus. Now everything works great!
stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame)
For anyone looking into using AS3 to develop desktop games for mac or PC (as in Steam), this is great.
the game Machinarium must have done something like this, because the ESC key never causes this effect, event though it was made in flash.

Pressing space does weird things

I've made a simple game in flex. You control falling blocks and your goal is to eliminate viruses. It's almost a copy of the 90s game dr Mario. I've made it so you control blocks with the arrow keys and you spin the block with space. Everything works fine as it should when playing. However when i switch to another program and the application is out of focus and i get back to the game, whenever i press space the game restarts. It's like it calls a function that reinitializes the game and resets all the variables to the start values.
The game is made with several NavigationContent components that acts like scenes. The game doesn't go back to the start screen when i press space, it just resets the game. Wich is really weird.
Are there any default method that is called that causes this behavior? Anyone have a clue?
EDIT: The issue arises - as it seems - exclusively when i tie a function to the space key (keyCode 32). I solved the issue by rebinding the key to "CTRL". But still it would be great to know what's up with the SPACE key. The game works fine with space if i use Internet Explorer. Other browsers doesn't work with the space key. It's the same issue with all of them.
EDIT: This is how the event listener looks:
this.stage.addEventListener(KeyboardEvent.KEY_DOWN, moveBlocksKeyboardEvent);
Even if i comment out all the code in the moveBlocksKeyboardEvent method the game still restarts. It's exclusively when hitting the SPACE-key. If i hold down the key the blocks spin. It's when i release the space button the game restarts. As if it's some reinitialization method tied to the KEY_UP event or something.
This type of behaviour is often tied to a null or undefined value, causing a nonsense-code jump which then results in a reset.
Make sure that that event handler for key down is attached to a valid object; if you are using "stage" then make sure it exists. When you move out of focus, the event handler may be left associated with a null object; when you reenter, it doesn't exist anymore, and therefore you get the reset behaviour.
This thread may help provide more detail:
Adding a key listener in Action Script 3

EventDispatcher between an as and an fla?

I am making a fighting game in Flash and while I have everything running, I am missing something: a victory/loss screen. Logically, I know how to do it:
if character.hp < 0
{
character.dead = true;
dispatchevent("death", event)
}
My problem is that I have no idea as to how to code it. I know I will use two classes and my two .fla files (unless I am wrong).
I have two .fla files that are in play here: the Menu.fla file and the Arena.fla file. Menu.fla contains the entire navigation of the game, options, character selection screens, etc. and when it is time for the player to engage in battle, it loads the Arena.fla file, which contains only the backgrounds (depending on the selected stage) and for now is set to a length of one frame only. For Arena.fla, the real action happens in my classes, but logically, I would only need HP.as and Character.as.
In Character.as, I have declared the following variable:
var isDead:Boolean = false; //is character dead?
In HP.as, believe I should have the following:
if(currentHp<0)
{
currentHp = 0;
character.isDead = true; //declared as var `character:Object;`
EventDispatcher.dispatchEventListener("playerDead", playerDead);
}
And finally, in Arena.fla, I want to be able to detect the above-mentioned eventlistener and simply move on to a second frame which will display a message in the style of "PLAYER ONE HAS WON" or "PLAYER ONE HAS LOST" with a button that will allow me to go back to the character selection screen. This is the first part in which I am stuck: how do I detect the dispatched event listener in my main .fla file?
Secondly, if the player clicks on the "CONTINUE" button, which displays regardless if the player has won or lost, how can my Menu.fla (which loads the Arena.swf) detect this click event, unload the game, and go back to the character selection screen?
Thank you in advance for helping me out. I realize this is a lot of text but it's the most descriptive I can be. If you have any questions or need any clarification concerning my question, feel free to speak up.
-Christopher
I'm not sure about the code you have to read the HP but do you know that character.dead is actually becoming true?
You could always have the Arena.swf call a function in the HP.as that will end the game and declare a winner.You could add a second Frame to Arena.swf that contains a dimmed background and a WINNER or LOSER text.'
In general, the easiest way for a user-defined class to gain event dispatching capabilities is to extend EventDispatcher. If this is impossible (that is, if the class is already extending another class), you can instead implement the IEventDispatcher interface, create an EventDispatcher member, and write simple hooks to route calls into the aggregated EventDispatcher.
activate
Dispatched when Flash Player or an AIR application gains operating system focus and becomes active.
deactivate
Dispatched when Flash Player or an AIR application loses operating system focus and is becoming inactive.
Event dispatcher
Thank you all for your help, but I have figured it out. Turns out my method was far too complicated for what I wanted to do, and for the time I had left. I will explain how I did it.
Instead of using an EventDispatcher like I thought I would, I used a SharedObject, which simply made everything work like magic.
A SharedObject can be accessed from anywhere in the application/game, as long as it is referred to it correctly. So I simply created a SharedObject called "winLossData" set to "NO WINNERS" in my character selection screen. This cookie is never saved nor written to the disk, so there's no chance for the user to find it (generally speaking).
I have decided to use the Movement.as class which contains all of my controls and wrote an event listener of type Event.ENTER_FRAME that checks constantly my characters' health status. If one of them is below 100, my SharedObject immediately takes for value either "PLAYER ONE" or "PLAYER TWO", depending on who won (i.e. whose health points are not under 100). Afterward, just for precaution, I reset the losing character's health points to 100. Here's the code:
function whoWon(event:Event):void
{
if(playerSpriteBar.getPower() <= 0)
{
winner.data.winner = "Player Two";
playerSpriteBar.update(100);
}
if(playerAIBar.getPower() <= 0)
{
winner.data.winner = "Player One";
playerAIBar.update(100);
}
}
In my Menu.fla, I have another event listener of type Event.ENTER_FRAME that waits for the cookie to change value. As soon as the cookie changes values, Menu.fla automatically unloads the external swf (in our case, Arena.swf) and displays the results, accordingly to the received SharedObject. The rest of the actions happen inside the Menu.fla file, so no need for any extra coding.
Once again, thank you all for your help.