AS3: Advanced Keyboard Detection
----------------------
AS3: Main
----------------------
Tutorials Pertaining to This:
Arrays, Vectors and Dictionaries
Static Keyword, The
----------------------
Note:
Throughout this tutorial I will assume that you are already competent with event listeners.
Another Note:
I intentionally wrote this tutorial so that you will not be able to copy/paste code; I will not be providing any complete source code.
If you want to copy/paste working code, and not learn anything, go here.
Keyboard Detection
In ActionScript 3, all keyboard detection is handled through event listeners.
You create an event listener for when a key is pressed down, and one for when a key is released.
Inside those listeners you write code to do magic stuff that can be turned into useful functions:
if (Key.isDown(Key.A))
{
trace("You pressed the A key!");
}
if (Key.isDown([Key.A, Key.S, Key.D, Key.F])
{
trace("You landed the ASDF combo!");
}
This tutorial will teach you how to create the above functions.
What makes this "advanced"?
I am going not only going to teach you how to detect when a key is down; I am also going to each you how to detect when a key was just pressed, and when a key was just released.
The code required is not actually much more complicated; I just don't know what else to call it, so "advanced" it is.
Basics - Part 1: Creating the Event Listeners
You must attach the event listeners to an object in order for them to be function; kinda goes without saying.
You should always attach the event listeners to the stage. Typically during setup:
package
{
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.display.Sprite;
public class Main extends Sprite
{
public function Main():void
{
if (stage) onAddedToStage(null);
else addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true);
}
public function onAddedToStage(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
//
// create the keyboard event listeners
//
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false, 0, true);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp, false, 0, true);
}
}
}
The two lines of note are here:
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false, 0, true);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp, false, 0, true);
These are your keyboard event listeners.
The KeyboardEvent object contains a public property keyCode.
That is the ASCII value of the key that was pressed/released. You can view a list of the ASCII key codes here: http://people.uncw.edu/tompkinsj/112/flashactionscript/keyco des.htm.
Basics - Part 2: Do Stuff When You Press Stuff
The next step is to create the onKeyDown and onKeyUp functons.
They must take one input parameter that is of type KeyboardEvent. In their most basic form the functions look like this:
public function onKeyDown(event:KeyboardEvent):void
{
//
}
public function onKeyUp(event:KeyboardEvent):void
{
//
}
Inside those functions you will write code to set key states. Specifically: a boolean.
Here is an example class that will track the state of any key that is pressed or released:
import flash.utils.Dictionary;
public class Keyboard
{
static private var __mKeyStates:Dictionary = new Dictionary;
static public function onKeyDown(event:KeyboardEvent):void
{
__mKeyStates[event.keyCode] = true;
}
static public function onKeyUp(event:KeyboardEvent):void
{
__mKeyStates[event.keyCode] = false;
}
}
However, that alone will not provide functionality to check the state of keys that were just pressed or just released.
That code will only allow you to check if a key is down or not; nothing else.
Putting it All Together:
Note:
In this section I am going to be knowingly providing some code that doesn't work, and then explaining why it won't work.
This is to help you understand why you have to do things a certain way.
Now that the basics have been covered we can get into the fun stuff: determining different states.
In order to track when keys are down, just pressed and just released you need to create three containers to store said states in.
A Dictionary container makes the most sense (see here).
So, your code will look like this:
public class Keyboard
{
static private var __mKeysDown:Dictionary = new Dictionary;
static private var __mKeysPressed:Dictionary = new Dictionary;
static private var __mKeysReleased:Dictionary = new Dictionary;
}
The variable names are only my personal preference; they can be whatever you want.
Though I reccomend you use names that clearly differentiate key down states from key just pressed states.
Plugging those new containers into the functions above is trivial:
static public function onKeyDown(event:KeyboardEvent):void
{
__mKeysDown[event.keyCode] = true;
__mKeysPressed[event.keyCode] = true;
}
static public function onKeyUp(event:KeyboardEvent):void
{
__mKeysDown[event.keyCode] = false;
__mKeysReleased[event.keyCode] = true;
}
If you actually try using this code you will find out that it doesn't work.
And here's why: The key just pressed and key just released states are being set at the appropriate time. However, they should only be true for a single frame; after that they need to reset. Otherwise they will be providing erroneous information.
So, let's create a reset function:
static public function reset():void
{
__mKeysPressed = new Dictionary;
__mKeysReleased = new Dictionary;
}
This should work, right? Call reset at the end of the frame, and the states are completely reset.
Well, no; this will also not work. Here's why: the states for just pressed cannot be reset in that manner. That's because the onKeyDown function gets called continuously when a key is held down (technically you have to hold the key down for 2 seconds before that happens).
This will end up causing the just pressed state to be identical to the is down state.
That means that you need to follow this logic:
Is the key down?
Yes: Is the state of the key currently "is just pressed"?
Yes: Then ignore it.
No: Set the key's state as "just pressed".
No: Then ignore it.
The code for that is actually simpler than it sounds:
__mKeysPressed[event.keyCode] = !(event.keyCode in __mKeysPressed);
This will only set the state to true if it currently does not exist in the Dictionary.
Will be set to false otherwise.
Now what about the just released state? Does that also need that code?
The answer is no, because the onKeyUp function is not called continuously like onKeyDown is.
In fact, since you know that, you can take advantage of it:
static public function onKeyUp(event:KeyboardEvent):void
{
delete __mKeysDown[event.keyCode];
delete __mKeysPressed[event.keyCode];
__mKeysReleased[event.keyCode] = true;
}
This will ensure that whenever a key is released it is removed from the is down and just released states.
Now, will this work? You can probably guess what the answer is: nope.
That's because there is one last piece of logic that is missing inside the reset function.