Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Multiple Key Detection

code by Michael Avila, written by Kirupa   | filed under Flash and ActionScript

This is an archived tutorial from the kirupa.com legacy collection. It covers software that may no longer be available, but it is kept online because the ideas still hold up.

If you frequent the Source/Experiments forum, chances are that you have run into Michael Avila's (MichalxxOA) experiments. This tutorial is based on Michael's Multiple Key Detection script, and by reading this tutorial, I hope you will learn not only how Michael created the multiple key detection functionality but also tips on how you can use it and modify it for your own projects.

Whenever you use keyboard shortcuts, you are holding and pressing several keys consecutively. Have you ever thought about how this functionality actually works? What causes your computer to realize that when you press the N key while holding down the Ctrl key, you meant to type Ctrl + N as opposed to each key separately?

While the answers to the above questions depend largely on the implementation of the multiple key detection, the overall idea behind them is the same - keep track of keys pressed and check whether the pressed keys map to a valid shortcut.

The following is an animation implementing the Multiple Key Detection script you will learn about in this tutorial. Click on the GO button in the animation below and press the Ctrl + K keys to randomly keep displaying the letter K:

[ click GO in the above animation and press Ctrl + K ]

Beyond explaining how to detect multiple keys, this tutorial also covers the following high-level topics:

If you are a beginning Flasher, this tutorial might be a little complicated. Don't worry though, the following tutorials on Object Oriented Programming and Arrays should prepare you well for what lies ahead in the following sections. I will also provide enough tips and clarifications so that you won't be lost.

The tutorial is broken into the following sections:

  1. How to Use
  2. Code Explanation
  3. Conclusion

The following sections will take you through each of the sections and hopefully teach you something that you might not have known about before.

In the previous section, you got a brief glimpse at what cool things this tutorial will show you. Let's start by first explaining how you can implement this effect into your own animations.

How to Use

It is very simple to incorporate the Multiple Key Detection feature into your projects.

Note

If you are fairly familiar with Flash, read the directions in Michael's post, and feel free to skip over to the end of the next section.

If you are a little overwhelmed with all of this, the following paragraphs should help you get started. First, download the KeyDetection.as file from the following link:

After downloading the file from the above link, extract the KeyDetection.as file to the location you want your Flash animation to be located. For simplicity, your Flash project and KeyDetection.as file must be located in the same folder.

With your animation created, the following is an example of the code you will be using:

var keyDet = new KeyDetection();
keyDet.addCombination("letterK", Key.CONTROL, 75);
keyDet.addCombination("letterJ", Key.CONTROL, 74);
myObj = new Object();
myObj.onKeyCombination = function(name:String) {
  switch (name) {
  case "letterK" :
  duplicateK();
  break;
  case "letterJ" :
  duplicateJ();
  break;
  }
};
keyDet.addListener(myObj);

In the above code, I left only the important parts of the code colored, for those are the lines that you will want to modify to suit your animation. Let's go through what some of the more important parts of the code do.

To add a key combination, you would use the following format:

keyDet.addCombination("caseName", Key,...,Key)

The object keyDet is a KeyDetection() object as seen in the first line of code above. You will use the addCombination method to input your key combination's name and keys.

The value for caseName corresponds to what you want to call your particular combination. I will explain the significance of the caseName in a short bit. To better help you understand how to add key combinations, I will provide several examples.

Ctrl + K:

keyDet.addCombination("letterK", Key.CONTROL, 75);

Ctrl + J:

keyDet.addCombination("letterJ", Key.CONTROL, 74);

Ctrl + Shift + S:

keyDet.addCombination("save as", Key.CONTROL, Key.SHIFT, 83);

The numbers 75, 74, and 83 are the corresponding ASCII codes for the letters K, J, and S. Also, notice that in the last example I have a key combination involving three keys instead of the traditional two key method.

For a list of ASCII codes and the characters they map to, the following table (courtesy of Wikipiedia) should help you:

ASCII Code Letter
65 A
66 B
67 C
68 D
69 E
70 F
71 G
72 H
73 I
74 J
75 K
76 L
77 M
78 N
79 O
80 P
81 Q
82 R
83 S
84 T
85 U
86 V
87 W
88 X
89 Y
90 Z
 

Adding key combinations is just one part of the equation. In the next section, you'll learn how to specify events that will be triggered for specific key combinations.

In the previous section, you learned how to specify key combinations. But that is only part of the fun. We need to specify what needs to be done when a certain key combination is pressed. For that, we look at the switch statement in the code:

switch (name) {
  case "caseName1" :
  //something
  break;
  case "caseName2" :
  //something
  break;
}

Replace caseName1 and caseName2 with the caseName value you specified in your addCombination arguments. When a key combination is executed, the appropriate switch case will be fired. Any code that is contained in that particular case will be executed. One thing to remember, though, is that you will need a separate case for every key combination you specify.

While in the above example I only provided two cases, you can have as many cases as you want. Just remember to give your caseName values a unique name, or else Flash won't know which case to fire.

For the most part, the code you need to use the key detection is straightforward. I want to take a big u-turn and talk about the actual code in the KeyDetection class file. For, I think that is where the real action is!


Code Explained

Learning how to use the code is just one part of this tutorial. The other major part involves learning why the code works. Let's start at the top of our KeyDetection.as file:

// a list of all the key codes that have been pressed
private var keys_pressed : Array;
// a multi-dimensional list of all of our key combinations
private var key_combinations : Array;
// objects listening to this detection
private var listeners : Array;

The keyDetection class contains three private variables, keys_pressed, key_combination, and listeners, that will be instantiated for each object created.


public function KeyDetection ()
{
  keys_pressed = new Array ();
  key_combinations = new Array ();
  listeners = new Array ();
  // allow this object to listen for events from the key object
  Key.addListener (this);
}

The above code is the constructor used to create KeyDetection objects. The constructor basically creates the object. Very little heavy lifting goes on here. The main thing to note is that the private variables we declared earlier are initialized here, and that ensures that a local copy of these variables are attached to the object being created by the constructor.

In the last line, I add this object to the Key's addListener method. The this variable inside the constructor refers to the object being created by this constructor.


public function addListener (listener : Object) : Void
{
  for (var i : Number = 0; i < listeners.length; i ++)
  {
  if (listeners [i] == listener) return;
  }
  listeners.push (listener);
}

If your listeners array does not contain the object being passed in, then you add this object to your listeners array. This method is called by your Key.addListener line in the constructor.


We have barely scratched the surface of the code. There is more in the next section!

In the previous section you learned about the constructor and addListener methods. You will see how they work together with the code presented on this page and subsequent pages.

public function removeListener (listener : Object) : Void
{
  for (var i : Number = 0; i < listeners.length; i ++)
  {
  if (listeners [i] == listener) listeners.splice (i, 1);
  }
}

The removeListener method does the exact opposite of your addListener function. It too takes an Object as its argument, but it removes the Object if it finds it in the listeners array. Flash does not have a remove method for arrays, but you can simulate a remove function by using the splice method:

listeners.splice (i, 1);

public function addCombination (name : String, keyCode1 : Number, keyCode2 : Number) : Void
{
  key_combinations.push (arguments);
}

This method takes in the three arguments that make up your multiple key combination. Notice how the arguments are being added to the key_combinations array. Instead of pushing them into the array one variable at a time, the keyword 'arguments' is passed in instead.

The arguments keyword captures all of the data passed into your method. You can use this approach when you can't accurately predict the amount of arguments that you might get passed into a method. For example, you may have key combinations that require more than two keyboard presses such as Ctrl + Shift + N, for example.

The final format of the arguments will be (String, Number, Number). When you pass in, for example, Key.Control, the ASCII-code equivalent is passed in instead. A sample trace of the arguments path for Ctrl + Y would be:

redo,17,89

Let's now take a look at how your key_combinations actually stores your data. If you were to add Ctrl + Z and Ctrl + N, as your combinations, you would use the following code in your FLA:

keyDet.addCombination("undo", Key.CONTROL, 90);
keyDet.addCombination("redo", Key.CONTROL, 89);

Each of the arguments you made to the addCombination method is stored in the key_combinations array as a nested array item. Your array basically resembles the following structure:

							[[undo,17,90],[redo,17,89],[new,17,85]]

private function invokeOnKeyCombination (combo_name : String ) : Void
{
  for (var i : Number = 0; i < listeners.length; i ++)
  {
  listeners [i].onKeyCombination (combo_name);
  }
}

This function takes in the name of your combo, combo_name, as the argument. It then cycles through all of your listener objects and passes the combo_name argument to the onKeyCombination method of each listener object. If you recall, the onKeyCombination method is defined in your FLA.

More code explanations in the next section!

We started to go through the hefty parts of the code in the previous section. In this page, we will chug on and trace how our key detection actually maps what you type to what you specified as a trigger in your FLA.


private function onKeyDown ()
{
  var key : Number = Key.getCode ();
  cleanKeysPressed ();
  if (key != keys_pressed [keys_pressed.length - 1])
  {
  keys_pressed.push (key);
  }
  checkCombinations ();
}

This function is responsible for checking which keys you have pressed and adding them to your keys_pressed array. First you call the cleanKeysPressed() function to clear your past history of key presses, and then you cycle through your keys_pressed array to make sure the key you just pressed hasn't been pressed consecutively.

In other words, you can press Ctrl + Z + N + Z continuously, but you will not be allowed to press Ctrl + Z + Z. Also, your keys_pressed array stores only the letters that you press in a combination. Pressing the Ctrl key, releasing the Ctrl key, and pressing the Z key again will not cause your keys_pressed array to store the keycode for both your Ctrl and Z key presses.


private function checkCombinations ()
{
  for (var j : Number = 0; j < key_combinations.length; j ++)
  {
  for (var i : Number = 0; i < keys_pressed.length; i ++)
  {
  if (keys_pressed [i] == key_combinations [j][i + 1])
  {
  if (i == key_combinations [j].length - 2)
  {
  invokeOnKeyCombination (key_combinations [j][0]);
  return;
  }
  } else
  {
  break;
  }
  }
  }
}

This function is where a lot of the action takes place! The checkCombinations function uses two nested for loops to compare whether the key combinations you specified match the keys you pressed.

The best way to explain this is via an, albeit long, example:

Trial 0.0

Let's say that you pressed the key combination Ctrl + Z for Undo. Your key_combinations array, for example, contains the following data:

key_combinations -> [[redo, 17, 89], [undo, 17, 90], [paste, 17, 86]];

Because you pressed Ctrl + Z, your keys_pressed array will contain the codes for your Ctrl and Z keys:

keys_pressed -> [17, 90];

The code's goal is to realize that you pressing Ctrl + Z (Key 17 + Key 89) maps to your key_combination for [undo, 17, 90]. So, let's try to see how the above lines of code help you to interpret the key presses as Undo.

This is a pretty involved function, so take a quick break and let's continue in the next section!

In the previous section, I briefly framed the problem associated with getting our checkCombination method to work. Let's continue our step-by-step trace of how it works in this page.

First, let's go through the nested for loop:

for (var j:Number = 0; j<key_combinations.length; j++) {
  for (var i:Number = 0; i<keys_pressed.length; i++) {
  if (keys_pressed[i] == key_combinations[j][i+1]) {
  if (i == key_combinations[j].length-2) {
  invokeOnKeyCombination(key_combinations[j][0]);
  return;
  }
  } else {
  break;
  }
  }
}

Initially, j is initialized to zero. In the second loop, i is initialized to 0 also. We are now at the if statement:

if (keys_pressed[0] == key_combinations[0][1])

I substituted a 0 for both the i and j variables in the if statement. keys_pressed[0] maps to the number 17 from our keys_pressed array. If you recall, The key_combinations array is a nested array. By calling key_combinations[0][1], I am calling the first array's second item - which is 17. Success!

Because the previous if statement equated out to true, we go to the second if statement (with i and j values substituted in):

if (0 == key_combinations[0].length - 2)

The length of our key_combinations[0] array ([redo, 17, 89]), is three. So, 3-2 is 1, and 0 does not equal 1. This conditional results in false. Let's go back to our second for loop and try again!

Trial 0.1

So, we are back at the second for loop. The value for j remains the same at 0, because the first for loop is what controls the value of the j variable. But the value of i will increment (i++). It is now 1. Now, our first if statement is:

if (keys_pressed[1] == key_combinations[0][2])

The maps out to if (90 == 89). Nope - that does not work! We jump down the break statement and exit out of this loop. We are now back at the first for loop. Let's try again!

Trial 1.0

So, we are back at (almost) where we started from - the first for loop. This time, the value of j is set to 1 instead instead of 0. We proceed to the second for loop, and we start back with i being equal to 1. Now, let's take a look at the first if statement again:

if (keys_pressed[0] == key_combinations[1][1])

Notice that the value of j is 1, so we will be looking at the second item in our key_combinations array. More specifically, we will be looking at key_combinations' second item's second item:

key_combinations' second item:

[undo, 17, 90]

key_combinations' second item's second item:

Similar to before, our if statement checks out. keys_pressed[0] is 17 and so is the value returned by our key_combinations[1][1] array. We then go to the second if statement:

if (0 == key_combinations[1].length - 2)

Like a recurring bad dream, though, you will unfortunately have the equality 0 == 1, which is false. Even though our j value is incremented by one, it still refers to an item of length 3.

Phew! We are still not done yet. Let's cover some more of our checkCombinations code in the next section and wrap this tutorial up!

In the previous section, we dived through more of our checkCombinations method. Let's pick up from where we left off and go back to our second for loop and try again. The value of j is still 1, but the value of i is now 1:

if (keys_pressed[1] == key_combinations[1][2])

Just like before, when we map the values referenced by our array indices, we get:

if (90 == 90)

This equates to true! So far so good. Let's proceed on to the next if statement:

if (1 == key_combinations[1].length - 2)

This time, this too holds true. The length of our key_combinations[1] array minus 2 does equal 1. We now execute the following line of code:

     invokeOnKeyCombination (key_combinations [j][0]);

The value of j, of course, is 1. Substituting in the values from the key_combinations array, we get the following:

key_combinations -> [[redo, 17, 89], [undo, 17, 90], [paste, 17, 86]];

The item referenced by index position 1 (j = 1), is:

[undo, 17, 90]

The 0th object is the string value undo. In short, the invokeOnKeyCombination function takes in the argument undo. If you recall from earlier, the invokeOnKeyCombination simply maps your input to the case/switch statement you designated in your FLA file:

myObj.onKeyCombination = function(name:String) {
  switch (name) {
  case "undo" :
  message_txt.text = "Undo Combination Pressed";
  break;
  case "redo" :
  message_txt.text = "Redo Combination Pressed";
  break;
  case "new" :
  message_txt.text = "New Combination Pressed";
  break;
  }
};

A text field called message_txt will display the sentence "Undo Combination Pressed." Excellent. You are good to go!


Conclusion

I hope this tutorial helped you to incorporate key detection in your animations. More importantly, this tutorial should have provided your more practice with using, accessing, and manipulating data stored in nested arrays.

If you are interested in seeing my implementation of the animation on the beginning of this tutorial, I have provided the source file for it:

If you have any questions, please post them on the forums.

Michael Avila (MichaelxxOA)
CreateAge.com

Huge Grin

Kirupa Chinnathambi (kirupa)
kirupa | MIT

Just a final word before we wrap up. What you've seen here is freshly baked content without added preservatives, artificial intelligence, ads, and algorithm-driven doodads. A huge thank you to all of you who buy kirupa's books, became a paid subscriber, watch the videos, and/or interact on the forums.

Your support keeps this site going! 😇

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2026 //--