00:00
00:00
Newgrounds Background Image Theme

Robert0241 just joined the crew!

We need you on the team, too.

Support Newgrounds and get tons of perks for just $2.99!

Create a Free Account and then..

Become a Supporter!

As3: Advanced Keyboard Detection

9,700 Views | 15 Replies
New Topic Respond to this Topic

As3: Advanced Keyboard Detection 2012-03-23 14:23:48


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.

Response to As3: Advanced Keyboard Detection 2012-03-23 14:24:57


As I said earlier, the onKeyDown function will only be called continuously if the key is held down for 2 seconds.
That means during that 2 second window the just pressed state will remain true, which is not desired behavior.
So what we need to do is in the reset function set every element in the __mKeysPressed container to false:

static public function reset():void
{
	__mKeysReleased = new Dictionary;
	for (var index:String in __mKeysPressed)
		__mKeysPressed[index] = false;
}

And that's it! You are not tracking all three key states.
However, without a way to check those states, this isn't very useful. So, on to the next section.

Note:
You will need to call the reset function at the end of your main update loop.
Typically it should be the very last function called.

Checking the Key States
The simplest solution would be to create the following three functions:

static public function isDown(key:int):Boolean
{
	return (key in __mKeysDown);
}

static public function isPressed(key:int):Boolean
{
	return (key in __mKeysPressed) && (__mKeysPressed[key]);
}

static public function isReleased(key:int):Boolean
{
	return (key in __mKeysReleased);
}

The isPressed function is different because it is possible for an element to contain a false state.
The __mKeysDown and __mKeysReleased containers, however, will only ever contain true states.

Personally I find this solution to be inadequate for two reasons:
1) It's repeating code.
2) It only supports checking one key at a time.

So we need to create a fourth function. Something like this:

static private function __getState(haystack:Dictionary, args:Array):Boolean
{
	//
}

Where haystack is one of the three dictionaries, and args is an array of each key code to check.
A vector container would also work for the args, but I prefer an array because of the syntactical differences:

__getState(__mKeysDown, [Key.A, Key.B]); //array
__getState(__mKeysDown, new Vector.<int>([Key.A, Key.B])); //vector

Because of the context you can't really make a wrong decision.
It's up to your preference.

Now, as for the logic of the __getState function:
You will need to iterate through the haystack container.
Only return true if all keys defined in args are found.

Here is an inefficient example:

static private function __getState(haystack:Dictionary, args:Array):Boolean
{	
	var matchesFound:uint = 0;
	
	for (var index:String in haystack)
	{
		for each (var key:int in args)
		{
			if (key == int(index) && haystack[index])
			{
				matchesFound++;
			}
		}
	}
	
	return (matchesFound == args.length);
}

Note:
When I say that this is inefficient I really mean it. Don't use this code. It's only an example.
If you desire this functionality then you will need to re-write this. That is outside the scope of this tutorial though.

That will iterate through each element in the haystack container.
For each of the elements in the dictionary the args container will also be iterated. If the keys match, and the dictionary element is set to true, then the total number of matches increments.
Pretty basic stuff, really.

Then you need only alter the other three functions slightly:

static public function isDown(args:*):Boolean
{
	if (args is int) return (args in __mKeysDown);
	else return __getState(__mKeysDown, args);
}

static public function isPressed(key:*):Boolean
{
	if (args is int) return (args in __mKeysPressed);
	else return __getState(__mKeysPressed, args);
}

static public function isReleased(key:*):Boolean
{
	if (args is int) return (args in __mKeysReleased);
	else return __getState(__mKeysReleased, args);
}

If the passed argument is of type int then a simple (key in container) is a perfect solution.
Should a different type be passed, presumably an array, then return the value of __getState.

Summary
Hopefully you've learnt some new techniques for tracking keyboard input.
I did not plan on this tutorial being this long, but it ended up being that way.

There is definitely a lot of moving pieces to this tutorial, and I did my best to write it in a clear and concise manner, however if there is anything that is unclear just ask for verification. I'd be happy to help.

Note:
I did not cover how to have values such as Key.A, Key.RIGHT or Key.F3.
That is just a class with a bunch of static public members: http://pastebin.com/WRUmLi1N.

Response to As3: Advanced Keyboard Detection 2012-03-23 14:41:10


Excellent tutorial.
Just from yesterday I was fighting with this, and my problem was, that after clicking something, I stop receiving keyboard events, and after a lot of test, I find the problem was clicking a TextField, and when it has the focus, it stop the bubbling keyboard events, so I have to do stage.focus = null after clicking on TextField.

Response to As3: Advanced Keyboard Detection 2012-03-23 23:14:22


This was hard to follow man. I would recommend cleaning up your presentation. The sudden jump to static methods with what seemed like no explanation (unless you are trying to count another one of these guides). In the end someone has to piece together what the code will look like by the end.

Besides the flow of the document, I don't agree with your proposed usage of isPressed and isReleased information. For most keyboard input you are checking during your main loop to see if a key is pressed for that specific moment. In this case the implementation of isDown will be perfect because you are using your loop event to check the state of something.

The isPressed and isReleased should not be looked for inside of a loop, because they are singular events that coincide with the KEY_DOWN and KEY_UP events. For this reason, your implementation is flawed. The better solution is pass along the KEY_DOWN or KEY_UP events so that any part of the program will be able to get and use this information as soon as it is available.

Here is my untested, but will-likely-work edition of a KeyboardListener class that should be able to pass along events when needed, test for currently down keys, and still be statically accessible. The only thing it doesn't do is return focus to the stage if focus is lost.

package {

	import flash.events.EventDispatcher;

	public class KeyboardListener extends EventDispatcher {
		//  Vector, Dictonary, or Array it doesn't matter.
		private var keys:Array = new Array(256);
		private var keyPressed:Array = new Array(256);
		private var keyReleased:Array = new Array(256);
		
		public function KeyboardListener() {}
		
		/**
		*  KEY_DOWN listener.
		*/
		private function onKeyDown(event:KeyboardEvent):void {
			keys[event.keyCode] = true;
			
			//If this object has an event listener to it, and the key has been registered, then pass along the event.
			if (this.willTrigger(KeyboardEvent.KEY_DOWN) && keyPressed[event.keyCode]) {
			
				this.dispatchEvent(event);
				
				//In order to only pass along once, reset to false.
				keyPressed[event.keyCode] = false;
			}
		}
		
		/**
		*  KEY_UP listener.
		*/
		private function onKeyUp(event:KeyboardEvent):void {
			keys[event.keyCode] = false;
			keyPressed[event.keyCode] = true;
			//If there is a listener for the KEY_UP Event, pass it along.
			if (this.willTrigger(KeyboardEvent.KEY_UP)) {
				this.dispatchEvent(event);
			}
			//If this object has an event listener for KEY_DOWN, and keyPressed either does not exist or does has been set to false... 
			if (this.willTrigger(KeyboardEvent.KEY_DOWN) && !keyPressed[event.keyCode]) {
				
				//Reset keyPressed, so that it will have a chance to fire again.
				keyPressed[event.keyCode] = true;
			}
		}
		
		/**
		*  Check if a key is currently down.
		*/
		private function isKeyDown(keyCode:int):Boolean {
			var result:Boolean = false;
			if (keys[keyCode]) {
				result = true;
			}
			return result;
			//  The one line option:  
			//  return keys[keyCode]?true:false;
		}
		
		/**
		*  Provide a standard event callback function for any key that will be fired only on Press of a key.
		*/
		private function whenKeyPressed(callback:Function, keyCode:int):void {
			keyPressed[keyCode] = true;
			this.addEventListener(KeyboardEvent.KEY_DOWN, callback);
		}
		
		/**
		*  Provide a standard event callback function for any key that will be fired only on Release of a key.
		*/
		private function whenKeyReleased(callback:Function, keyCode:int):void {
			keyReleased[keyCode] = true;
			this.addEventListener(KeyboardEvent.KEY_UP, callback);
		}
		
		/*
		*  Static stuff so that you can get this information from anywhere.  As long as you 
		*  use the initialize function like so: 
		*    KeyboardListener.initialize({Object Stage)};
		*      {Object Stage} = the actual Stage for the flash project.
		*  
		*  Use the other static methods like so:
		*    KeyboardListener.methodName(parameters...);
		*/
		private static var self:KeyboardListener;
		public static function initialize(stage:Stage):void {
			if (self == null) {
				self = new KeyboardListener();
				stage.addEventListener(KeyboardEvent.KEY_DOWN, self.onKeyDown);
				stage.addEventListener(KeyboardEvent.KEY_UP, self.onKeyUp);
			}
		}
		public static function isKeyDown(keyCode:int):Boolean {
			return self.isKeyDown(keyCode);
		}
		public static function whenKeyPressed(callback:Function, keyCode:int):void {
			self.whenKeyPressed(callback, keyCode);
		}
		public static function whenKeyReleased(callback:Function, keyCode:int):void {
			self.whenKeyReleased(callback, keyCode);
		}
		public static function removeListener(keyDown:Boolean, callback:Function, keyCode:int):void {
			if (keyCode > -1) {
				if (keyDown) {
					self.removeEventListener(KeyboardEvent.KEY_DOWN, callback);
					self.keyPressed[keyCode] = false;
				} else {
					self.removeEventListener(KeyboardEvent.KEY_UP, callback);
					self.keyReleased[keyCode] = false;
				}
			}
		}
		
	}
}

Response to As3: Advanced Keyboard Detection 2012-03-23 23:31:42


//If this object has an event listener for KEY_DOWN, and keyPressed either does not exist or does has been set to false...
if (this.willTrigger(KeyboardEvent.KEY_DOWN) && !keyPressed[event.keyCode]) {

//Reset keyPressed, so that it will have a chance to fire again.
keyPressed[event.keyCode] = true;
}

Hindsight .... This if statement is useless. It can be changed to :

keyPressed[event.keyCode] = true;

As long as the key has been registered as "Up", then it has to be made available for "Down" again.

Response to As3: Advanced Keyboard Detection 2012-03-23 23:45:05


At 13 minutes ago, EuPlonKa wrote: This was hard to follow man. I would recommend cleaning up your presentation. The sudden jump to static methods with what seemed like no explanation

You are assuming that my target audience for this tutorial are beginners, which they are not.

At 13 minutes ago, EuPlonKa wrote: (unless you are trying to count another one of these guides).

Apparently you can't read; take a look at the header of the tutorial.

At 13 minutes ago, EuPlonKa wrote: In the end someone has to piece together what the code will look like by the end.

That is the entire point of the tutorial: to teach you how to code this yourself.
In the header I included a link to a "tutorial" written by deltaluca for people who don't want to learn.

At 13 minutes ago, EuPlonKa wrote: Besides the flow of the document, I don't agree with your proposed usage of isPressed and isReleased information. For most keyboard input you are checking during your main loop to see if a key is pressed for that specific moment. In this case the implementation of isDown will be perfect because you are using your loop event to check the state of something.

This doesn't make sense.
How can isDown provide the same functionality as isReleased?

You are also projecting your design philosophies.

At 13 minutes ago, EuPlonKa wrote: The isPressed and isReleased should not be looked for inside of a loop, because they are singular events that coincide with the KEY_DOWN and KEY_UP events.

By that logic there should be no isDown function either, which you, one paragraph prior, stated was a "perfect solution".
So, fix up that contradiction and then I will respond to this.

At 13 minutes ago, EuPlonKa wrote: // Vector, Dictonary, or Array it doesn't matter.

Actually it matters a lot.
Unless you don't care about the performance of your applications, then yeah, it doesn't matter.

Your code is also absolutely littered with cargo cult programming.
This function being a textbook example of it:

private function isKeyDown(keyCode:int):Boolean {
	var result:Boolean = false;
	if (keys[keyCode]) {
		result = true;
	}
	return result;
	//  The one line option:  
	//  return keys[keyCode]?true:false;
}

Response to As3: Advanced Keyboard Detection 2012-03-24 06:29:23


At 6 hours ago, Diki wrote: Your code is also absolutely littered with cargo cult programming.
This function being a textbook example of it:

private function isKeyDown(keyCode:int):Boolean {
var result:Boolean = false;
if (keys[keyCode]) {
result = true;
}
return result;
// The one line option:
// return keys[keyCode]?true:false;
}

What is 'cargo cult programming' ?


You can solve pretty much any problem you may have with AS3 by consulting the AS3 Language reference.

Response to As3: Advanced Keyboard Detection 2012-03-24 08:49:49


At 2 hours ago, ProfessorFlash wrote:
What is 'cargo cult programming' ?

cargo means load, or goods carried by something; cult means "A system or community of religious worship and ritual." "Obsessive, especially faddish, devotion to or veneration for a person, principle, or thing.".

I would describe it as the need to bloat. That function could have been written as:

private function isKeyDown(keyCode:int):Boolean {
	return keys[keyCode];
}

Effectively making the need for that private function obsolete.
Writing elegant, smart code is I think one of the best qualities you can learn as a programmer, for it wont make your projects slow you down as they get bigger.
It the end -besides design patterns and OOP structure- its all about your code style; how you adhere to coding conventions and the quality and maintainability of your code. Maybe someone should write a tutorial on this.

Response to As3: Advanced Keyboard Detection 2012-03-24 14:15:07


I'm using a detection method similar to flixel.

An array stores objects bearing status and last properties, and each hold a value of 2, 1, 0, or -1.
2 = Pressed, justPressed
1 = Pressed
0 = Not pressed
-1 = justReleased

// Properties
_total = 256
arr;

// Construct
arr = new Array(_total)
fill arr with: {current:0,last:0}

// Update
Since "get" code is executed after update function, the "last" property is needed to make sure justPressed and justReleased proc.
if (status && last == -1) status = 0;
if (status && last == 2) status = 1;
last = status;

// Modify
keyDown(keyCode){arr[keyCode].status = (status>0)?1:2;}
keyUp(keyCode){arr[keyCode].status = (status>0)?-1:0;}

// Use
hold:Boolean {return status > 0;}
press:Boolean {return status == 2;}
release:Boolean  {return status == -1;}

The downside for this is looping through array of 256 size on each update.


Check out the Flash RPG I made in 2024. It takes about 25 minutes to complete.

BBS Signature

Response to As3: Advanced Keyboard Detection 2012-03-25 03:52:26


At 3/23/12 11:45 PM, Diki wrote:
At 13 minutes ago, EuPlonKa wrote: This was hard to follow man. I would recommend cleaning up your presentation. The sudden jump to static methods with what seemed like no explanation
You are assuming that my target audience for this tutorial are beginners, which they are not.

Why would you exclude a beginner?

At 13 minutes ago, EuPlonKa wrote: Besides the flow of the document, I don't agree with your proposed usage of isPressed and isReleased information. For most keyboard input you are checking during your main loop to see if a key is pressed for that specific moment. In this case the implementation of isDown will be perfect because you are using your loop event to check the state of something.
This doesn't make sense.
How can isDown provide the same functionality as isReleased?

You are also projecting your design philosophies.

I don't see how I said isDown and isReleased are providing the same functionality.

At 13 minutes ago, EuPlonKa wrote: The isPressed and isReleased should not be looked for inside of a loop, because they are singular events that coincide with the KEY_DOWN and KEY_UP events.
By that logic there should be no isDown function either, which you, one paragraph prior, stated was a "perfect solution".
So, fix up that contradiction and then I will respond to this.

No the states of pressed, down, and released are all exclusive.

Pressed is the single event of a key being pressed.
Down is if a key is in a current state of down.
Released is the single event of a key being let go.

Pressed and Released are singluar events. They exist only for that instant that the button is pressed or released.
Down is a state of being for a key. Keys are either in a state of down or up. We obviously only care about down, since the natural state of key is up.

The difference in what each event represents is extremely important in how we handle them. A singular event needs to be handled at the moment it occurs. If a singular event is to be tested at a later time, then it would have to be stored off with some sort of age/timestamp.

Checking the state of something is different. An event will change the state of something, but the event that caused the change is not the important part when we are concerned about state. So the question is not when the state was changed, but during what event are we going to check state.

At 13 minutes ago, EuPlonKa wrote: // Vector, Dictonary, or Array it doesn't matter.
Actually it matters a lot.
Unless you don't care about the performance of your applications, then yeah, it doesn't matter.

Your code is also absolutely littered with cargo cult programming.
This function being a textbook example of it:

private function isKeyDown(keyCode:int):Boolean {
var result:Boolean = false;
if (keys[keyCode]) {
result = true;
}
return result;
// The one line option:
// return keys[keyCode]?true:false;
}

Your choice of container for this is irrelevant. The time spent constructing this object is negligible, so speed/efficiency gains can only be made with read/write time. Array vs Vector for changing a Boolean at a specific index is negligible again, even reading time. Arrays have the advantage of not needing a value at every index, so you don't have to make a loop to place false in a Vector for all 256 locations. I haven't done testing on Object or Dictionary read/write times, but I would imagine creating a new property at run time may be costly.

For the 'cargo cult programming', I realize this is a design pattern of personal choice. I decided to put that in there because the inline if expression is considered hard to read, but was also why I included "the one line option" for those looking for a more efficient method.

The reason why I wrapped up the look up in an if statement was to ensure the method will return a Boolean. There is a chance that a strait property look up would return undefined if checked before a key is pressed. I was thinking that if it returns undefined, then it could possibly cause problems. I have tested it now, and found that these expressions should still return false, even if the value passed is undefined. So yeah, the if statement could be removed. For me though, I would still rather it return Boolean.

Response to As3: Advanced Keyboard Detection 2012-03-25 14:05:10


At 3/25/12 03:52 AM, EuPlonKa wrote: Why would you exclude a beginner?

Because if I do not then I will be excluding more advanced users.
You can't have your cake and eat it too.

At 3/25/12 03:52 AM, EuPlonKa wrote: I don't see how I said isDown and isReleased are providing the same functionality.

"I don't agree with your proposed usage of isPressed and isReleased [...] For most keyboard input you are checking during your main loop to see if a key is pressed for that specific moment [...] In this case the implementation of isDown will be perfect".

At 3/25/12 03:52 AM, EuPlonKa wrote: No the states of pressed, down, and released are all exclusive.
[...]

You seem to be confusing AS3 with a high performance language.
So, enlighten me, what is the inevitable erroneous behavior that will occur as a direct result of my design for keyboard input detection?

At 3/25/12 03:52 AM, EuPlonKa wrote: Your choice of container for this is irrelevant.

Again, if you are not concerned with the performance of said container, I agree.

At 3/25/12 03:52 AM, EuPlonKa wrote: The time spent constructing this object is negligible

I can say the same thing about the time between when a key is pressed and when an if statement is used with one of my functions.
Straw man arguments are fun.

At 3/25/12 03:52 AM, EuPlonKa wrote: Arrays have the advantage of not needing a value at every index, so you don't have to make a loop to place false in a Vector for all 256 locations.

Or you could just not store the key states of every single key on the keyboard at all times, negating the need to iterate through a container 256 times to find the state of 1-4 keys.

At 3/25/12 03:52 AM, EuPlonKa wrote: I haven't done testing on Object or Dictionary read/write times, but I would imagine creating a new property at run time may be costly.

Well, at least you can admit that you don't know what you're talking about.
The answer is: no; it's not costly by any stretch of imagination.

Sequentially creating 1,000,000 boolean elements in a dictionary takes approximately 260 milliseconds.
Sequentially creating 256 boolean elements in a dictionary takes microseconds and thus cannot be measured via AS3's getTimer function.

The functions in my tutorial will only ever create as many elements as keys that are currently in-use. So typically 1-4.
I would be willing to bet that that would drop down from microseconds to nanoseconds in terms of time required.

At 3/25/12 03:52 AM, EuPlonKa wrote: For the 'cargo cult programming', I realize this is a design pattern of personal choice. I decided to put that in there because the inline if expression is considered hard to read, but was also why I included "the one line option" for those looking for a more efficient method.

I am guessing you don't know what "cargo cult programming" means. It's not a design paradigm; it's a term used to describe poorly written code, generally by inexperienced developers (I am not accusing you of this; I do not presume to know why you wrote cargo cult code).
Almost everything in that code, and even your "more efficient" solution, is completely redundant and superfluous. It can be written as just this:

private function isKeyDown(keyCode:int):Boolean {
	return keys[keyCode];
}

There is no need for the ternary operator; the elements in the array are already boolean, and even if they were not they would be casted to a boolean value.

Your methods are using code that is literally not serving any useful purpose. That is what cargo cult programming is; it's something you're supposed to avoid doing.

At 3/25/12 03:52 AM, EuPlonKa wrote: The reason why I wrapped up the look up in an if statement was to ensure the method will return a Boolean.

No, you ensured that the function will return a boolean value by declaring the return value as boolean.
You know, at the end of this line:
private function isKeyDown(keyCode:int):Boolean.

Your if statement is just wasting resources; literally nothing more.

At 3/25/12 03:52 AM, EuPlonKa wrote: There is a chance that a strait property look up would return undefined if checked before a key is pressed.

Which will then be casted to a boolean false. What's the problem?

At 3/25/12 03:52 AM, EuPlonKa wrote: I was thinking that if it returns undefined, then it could possibly cause problems.

It won't.

At 3/25/12 03:52 AM, EuPlonKa wrote: I have tested it now, and found that these expressions should still return false, even if the value passed is undefined. So yeah, the if statement could be removed. For me though, I would still rather it return Boolean.

No, the statement should be removed; not could.
And, again, the function will always, with no exceptions, return a boolean value. You don't need a bunch of fancy hand-waving, superfluous, if statements. Just return the array's element.

Response to As3: Advanced Keyboard Detection 2012-03-25 18:58:16


So, enlighten me, what is the inevitable erroneous behavior that will occur as a direct result of my design for keyboard input detection?

KEY_DOWN fires the first time when a key is pressed, then waits aprox 300 milliseconds before firing again, if the key is still held down. Each remaining event (while the key is held down) will fire every aprox 18 milliseconds. What this means a key will be considered as "Pressed" for 300 milliseconds, unless a KEY_UP event is fired before then.

The problem becomes how you are trying to use this information. A 30 fps game loop will execute about once every 33 milliseconds, meaning a key will be considered "Pressed" multiple times. Which shouldn't be the case. "Pressed" is a one time singular event, not a state of a key. So in this case your loop would have to do a check to see if "Pressed" was checked already. So besides a game loop, what other event would be used to check "Pressed"?

In case of "Released" when do you propose someone use the reset method? You can't use it in the KEY_UP listener because nothing would be able to test for "Released". You can use some kind of timer to use it, but that is what I mean by placing some sort of age/timestamp to storing the event.

What I am trying to describe is that "Down" is a state of a key, while "Pressed" and "Released" are events that cause the state to change. They are not the same type of thing, and need to be treated differently. If you are wrapping up KeyboardEvents, and not passing them along in some form, then you have to choose another event to check the information.

You can check the state of something during any event, because the state of something has a beginning and an end based on the events that change the state. This is why the way you have described "Down" is perfectly fine, because it is a state. Events ("Pressed", "Released") are a single moment in time. States ("Down", "Up") are changed when an event occurs, and their timespan is based on the events that change them.

Response to As3: Advanced Keyboard Detection 2012-03-25 20:37:43


At 3/25/12 06:58 PM, EuPlonKa wrote: when do you propose someone use the reset method? You can't use it in the KEY_UP listener because nothing would be able to test for "Released". You can use some kind of timer to use it, but that is what I mean by placing some sort of age/timestamp to storing the event.

Did you even read what I wrote?
I explained very clearly where to place the reset function:

At 3/23/12 02:24 PM, Diki wrote: Note:
You will need to call the reset function at the end of your main update loop.
Typically it should be the very last function called.

And the rest of your post didn't even remotely address my question.
So, I will reiterate, and elaborate:
What erroneous behavior could occur as a direct result of the functions that I wrote?
What is a practical example of producing said errors?
How are they preventing custom events being created for esoteric situations where it is required?

Response to As3: Advanced Keyboard Detection 2012-03-25 23:54:03


What erroneous behavior could occur as a direct result of the functions that I wrote?
What is a practical example of producing said errors?
How are they preventing custom events being created for esoteric situations where it is required?

Take a typing game. I would want to capture which keys a person has pressed as soon as they press them, so I would be tracking the "Pressed" event.

If I am checking for "Pressed" during the game loop, and a person takes longer than the loop rate to take their hand off a key, then I would end up counting the key twice if not more. Obviously this would be unwanted behavior. What I would really want is to process the input from the keyboard as soon as it happens, and for only the first event of a key being pressed.

The chance is slim, but if KEY_UP fires after you have checked for "Released" but before reset(), then you have lost the chance to capture this event. Obviously this would be rare, since the window could literally be less than a millisecond. However, the goal should be no chance, not slim. Also a class should be self-contained, and not be dependent on something else whenever possible.

The class you are proposing would be there to keep track of the "Pressed" and "Released" events. However, if I wanted to process input as soon as a key is interacted with, I would have to add another KeyboardEvent Listener to the stage. KeyboardEvents, like EnterFrame events and Highlanders, there should be only one.

Response to As3: Advanced Keyboard Detection 2012-03-26 00:38:13


At 3/25/12 11:54 PM, EuPlonKa wrote: If I am checking for "Pressed" during the game loop, and a person takes longer than the loop rate to take their hand off a key, then I would end up counting the key twice if not more.

The code in my tutorial does not produce this error.
Keys are only considered "pressed" during the frame (or, the loop iteration) that they were pressed on.

At 3/25/12 11:54 PM, EuPlonKa wrote: The chance is slim, but if KEY_UP fires after you have checked for "Released" but before reset(), then you have lost the chance to capture this event.

This error is also not produced by the code in my tutorial.

So basically your argument boils down to that it would be possible to improperly code the design in my tutorial, I guess?

Response to As3: Advanced Keyboard Detection 2012-03-27 09:43:03


At 3/24/12 02:15 PM, Jin wrote: The downside for this is looping through array of 256 size on each update.

Just made a few adjustments to this, combining it with dictionary so hopefully this new format works better.
Code


Check out the Flash RPG I made in 2024. It takes about 25 minutes to complete.

BBS Signature