Hey, all! I'd like to share with you a keyboard input utility I recently made. I wrote it for myself, but I have decided to share it here, in the event that you might find it as easy, useful and convenient as I do. When you write code for keyboard input, there is certain functionality that you are going to always want. There are a few things that come to mind in this regard: First, you need to know whether a key is being held down or not. Secondly, you need to know if a key has just been pressed. Thirdly, you need to know if a key has just been released.
Let's focus on the second thing for a moment. The routine that detects if a key has just been pressed needs to only happen once per keypress. Let's think about the KeyboardEvent.KEY_DOWN event. This event, as it stands anyway, has a big difference from, say, a MouseEvent.CLICK event. The big difference is that the CLICK event gets dispatched only once per mouse click. The keyboard event keeps getting dispatched repeatedly, as long as you keep holding down the key. This effect is known as the keyboard repeat rate, and is actually part of your operating system. I have written about this before in the tutorial on making the left-right-shooter game. The solution is to use a Boolean variable for each key. Set this Boolean variable to true when you get a keypress from that key. Only set it to false again when that same key is released. Finally (and what makes it all work) use the value of this Boolean variable as part of your test to see whether you should process your key down event, like so:
if(event.keyCode == Keyboard.RIGHT && rightArrow == true)
Well, anyway, I am officially tired of writing code like this again and again every time I want to counteract the keyboard repeat rate! It seems like I ought to be able to just encapsulate this functionality into some kind of class I can use again and again. After checking out a couple of the keyboard managers that other people have come up with, I realized they were almost what I wanted but not quite. One of the best was by Senocular, but (as I recall) it satisified requirement 1, but didn't quite satisfy requirement 2 (requirement 3 is not as crucial, as there is no repeat rate involved there).
At first, I just modified Senocular's class and added in the functionality I wanted. I realized, though, that coding it was complicated by the fact that his key manager manages any number of keys, and uses an object to keep track of them all. This is fine for just keeping track if a key is down. I found myself bogging down trying to add the "just pressed" and "just released" parts, though, because I had to again make it capable of managing any number of keys. I was a little less than happy with the result, so I set about making my own KeyManager class. Again, though, I tried to write a class that handled any number of keys, using techniques similar to what Senocular had done (using generic objects to store the keys. Seems like there were static methods involved, too). And again I bogged down. It just seemed too complicated when it really shouldn't have been.
Then an inspiration came to me: Why not make a simpler class, whose sole responsibility was to provide the functionality I needed, but do it for just one key? Also, instead of a bunch of static methods, why not just let it be an ordinary class that you make instances of? The Boolean variable that I formerly created for each key could just be an instance variable in the class. Each instance of the class would get its own copy of this variable, so no more making multiple Booleans! This one simple (private) Boolean variable would be called _isDown. I could make it read-only from the outside by adding a public getter called isDown (without the underscore).
All you would have to do is to create an instance of this class for each keyboard key you want to program! Having to create mutiple instances is the only drawback I could see. But, in a typical application, you might only program a few keyboard keys anyway. And if you found yourself programming too many keys, you could automate it by using loops and arrays, but in my own use, I really don't anticipate that happening too often.
I also envisioned that the class would hold references for functions that it would call when a key is pressed or released, and that these could be written and set from outside the class. They would be written like ordinary keyboard event handlers. The difference is that the class would take care of nullifying the keyboard repeat rate for me. So unlike ordinary KEY_DOWN and KEY_UP event handlers, these functions would only be called once per key press/release.
One final functionality would be the capability to activate or deactivate an instance of the class. This would be done by setting an active property to true or false, and this would either add or remove the stage listeners depending on the Boolean value passed in. Instead of just deleting an instance of the class, then, you could turn it on and off at will.
So I called this class OneKeyManager, meaning that it's understood that this class manages exactly one keyboard key. Having been very happy with the result, I decided to share it. Whether you use it or not is up to you, but I can tell you that having created two previous key managers (albeit with some help on the first one), the third time seems to be the charm. I certainly intend to use it, and I believe that if you try it, you will probably like it too. And you certainly can't beat the price.
There is also another companion class, KeyList, and on the next page, I'll introduce you to both classes. Then, on the final page, I'll share with you how to use them effectively and give you a demo to download.
Tweening the player across the stage
Hi Jody,
Happy New Year, even though it's not that new!
Wonderful article/Tute. It's good to see some more goodies from you.
I am trying to add a tween using Greensock. But I'm getting an error message. See below.
ReferenceError: Error #1069: Property x not found on Number and there is no default value.
at com.greensock::TweenLite/init()
at com.greensock::TweenLite/renderTime()
at com.greensock::TweenLite()
at com.greensock::TweenLite$/from()
at one_key_manager_demo1_fla::MainTimeline/bSpace_onPress()
at OneKeyManager/_stage_onKeyDown()
I was trying to say use the BACKSPACE to tween the player across the stage. I've imported all Greensock classes, made a timeline instance and then made an instance of your OneKeyManager class for the BACKSPACE key, added an if condition for backSpace down to the loop function. I've added the following code to your Demo.
import com.greensock.*;
var tl:TimelineLite = new TimelineLite();
var backSpace:OneKeyManager = new OneKeyManager(stage, KeyList.BACKSPACE, bSpace_onPress, bSpace_onRelease);
function loop(event:Event):void {
if (backSpace.isDown){
tl.insert(TweenLite.to(player.x, 1, {x:+200}));
}
function bSpace_onPress(event:KeyboardEvent):void {
tl.play();
}
function bSpace_onRelease(event:KeyboardEvent):void {
}
Can you see where I'm going wrong?
Cheers,
Ben.
Found error, but improper use of TimelineLite
Ben,
Consider the arguments that TweenLite is expecting:
Tweenlite.to( objectToTween, duration, { vars } );
So the first mistake is that the objectToTween is not player.x, because player.x is not an object. The object to tween is the player. That's where your error about the number is coming from. But also, this looks funny to me:
x:+200
Seems like it should be:
x:"200" (by putting the value in quotes, you make it relative to it's current position)
Also, I tried running your code, but don't see where the enter frame handler ever gets added, so the program didn't do anything. Finally, I'm pretty sure you are putting the TimelineLite class to an improper use, you shouldn't be appending or inserting things to it every frame like that.
Jody
Success
Thanks Jody for your comments. They helped me solve these issues.
I don't know!!!Sometimes I think this coding stuff isn't for me!! Anyway.
I have it tweening now. Here's the code I changed. One little hitch was when I tested it using the BACKSPACE key on my keyboard nothing happened but I did get movement when using the Shift + Backspace key!
Thanks for pointing me in the right direction Jody.
Regards,
Ben.
var backSpace:OneKeyManager = new OneKeyManager(stage, KeyList.BACKSPACE);
this.addEventListener(Event.ENTER_FRAME, bSpace);
function bSpace(event:Event):void {
if (backSpace.isDown){
TweenLite.to(player, 1, {x:"-75", ease:Linear.easeNone});
}
Disable Keyboard Shortcuts
Ben,
If Backspace didn't work, you probably didn't disable the keyboard shortcuts on the pulldown Control menu on the running swf. This was mentioned in my article as something to be sure to check.
Jody
Easy, Fluid Keyboard Movement in AS3 With the Input Class
Hi Jody,
I happened to come across this tutorial that looks at keyboard movement. Thought you'd be interested.
http://bit.ly/yKR7Gc
Cheers,
Ben.
Thank you so much
Ben,
Thank you for that! I did find it very interesting! I'm an input freak!
Jody