Tony Polinelli
Tarwin Stroh-Spijer

contact [at]
touchmypixel.com

6/25 Easey Street
Collingwood, 3066
Vic, Australia

+61 3 8060 5321

Key events in AS3 and the missing Key.isDown()

When AS3 came out many people, myself including, were frustrated and confused by the lack of a quick and dirty function to check whether a key was down (pressed). You can have a listener that gets fired when a key is pressed and see which key it was within that function. This could lead to someone putting all their key behaviour code in one spot which could become quite messy.

The best way to fix this is with a class that does all the heavy lifting for you. Without any any further ado here's the test. Use the arrow keys, space, z and shift+z to try it out [source]:


The test test's code:

Actionscript:
  1. package test
  2. {
  3.     import com.touchmypixel.io.Keys;
  4.     import flash.text.TextField;
  5.    
  6.     import flash.display.MovieClip;
  7.     import flash.display.Sprite;
  8.     import flash.events.Event;
  9.     import flash.events.KeyboardEvent;
  10.    
  11.     public class TestKeys extends MovieClip
  12.     {
  13.         private var vx:Number = 0;
  14.         private var vy:Number = 0;
  15.         private var box:Sprite;
  16.         private var boxRed:Boolean = false;
  17.        
  18.         public var tText:TextField;
  19.        
  20.         public function TestKeys()
  21.         {
  22.             Keys.init(this);
  23.            
  24.             stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
  25.             stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownAgain);
  26.            
  27.             addEventListener(Event.ENTER_FRAME, ef);
  28.            
  29.             box = new Sprite();
  30.             drawBox();
  31.             box.x = 200;
  32.             box.y = 200;
  33.             addChild(box);
  34.         }
  35.        
  36.         private function keyDown(e:KeyboardEvent):void
  37.         {
  38.             if (Keys.isDown(Keys.SHIFT) && Keys.isDownOnce(Keys.Z, "SHIFT_Z")) {
  39.                 superTrace("Shift then Z pressed once");
  40.             }
  41.         }
  42.        
  43.         private function superTrace(s:String):void
  44.         {
  45.             tText.appendText(s + "\n");
  46.         }
  47.        
  48.         private function keyDownAgain(e:KeyboardEvent):void
  49.         {
  50.             if (Keys.isDownOnce(Keys.Z, "SHIFT_Z")) {
  51.                 superTrace("Z pressed once: SHIFT_Z");
  52.             }
  53.         }
  54.        
  55.         private function drawBox():void
  56.         {
  57.             var c:int = boxRed ? 0x00ff00 : 0xff0000;
  58.             box.graphics.beginFill(c);
  59.             box.graphics.drawRect(0, 0, 20, 20);
  60.             box.graphics.endFill();
  61.             boxRed = !boxRed;
  62.         }      
  63.        
  64.         private function ef(e:Event):void
  65.         {         
  66.             if (Keys.isDownOnce(Keys.SPACE, "CHANGE_BOX_COLOR")) drawBox();
  67.            
  68.             if (Keys.isDown(Keys.LEFT)) vx -= .5;
  69.             if (Keys.isDown(Keys.RIGHT)) vx += .5;
  70.             if (Keys.isDown(Keys.UP)) vy -= .5;
  71.             if (Keys.isDown(Keys.DOWN)) vy += .5;
  72.            
  73.             vx *= .95;
  74.             vy *= .95;
  75.            
  76.             box.x += vx;
  77.             box.y += vy;
  78.         }      
  79.     }
  80. }

and the class itself:

Actionscript:
  1. /*
  2. * Copyright 2008 by Touch My Pixe, Tarwin Stroh-Spijer and Tony Polinelli
  3. * Please feel free to use it in your own projects, but please let us know so we can bask in your love
  4. */
  5. package com.touchmypixel.io
  6. {
  7.     import flash.display.DisplayObject;
  8.     import flash.events.KeyboardEvent;
  9.  
  10.     /*
  11.      * Keys
  12.      * An easy way to handle key input
  13.      *
  14.      * Hint
  15.      * If you load an external SWF you may find that it takes over the key handling and you don't get any events here.
  16.      * You can fix this by setting the "stage.focus = stage", either on an event, or on a continuous loop.
  17.      */
  18.     public class Keys
  19.     {   
  20.         /* which keys are down (true)
  21.          */
  22.         static private var keycodes: Array = new Array(256);
  23.        
  24.         /* whick keys have been pushed down once, and not released
  25.          */
  26.         static private var keyDownUsed:Object = new Object();
  27.        
  28.         // object went through on init, used for deIniting
  29.         static private var initSprite:DisplayObject;
  30.        
  31.         /* this has to be called before
  32.          */
  33.         static public function init( stageSprite: DisplayObject ):void
  34.         {
  35.             // set as max value so this keyboard event is fired before any user set ones
  36.             stageSprite.stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDown, false, int.MAX_VALUE);
  37.             // set max-1 so it's always fired AFTER the key down
  38.             stageSprite.stage.addEventListener( KeyboardEvent.KEY_UP, onKeyUp, false, int.MAX_VALUE - 1);
  39.             initSprite = stageSprite;
  40.            
  41.             // clear for good housekeeping
  42.             clear();
  43.         }
  44.        
  45.         /* If you want to manually control which keys are up or down, for example running a recorded demo
  46.          * you can first run deInit, then
  47.          */
  48.         static public function deInit():void
  49.         {
  50.             if (initSprite) {
  51.                 clear();
  52.                 initSprite.stage.removeEventListener( KeyboardEvent.KEY_DOWN, onKeyDown );
  53.                 initSprite.stage.removeEventListener( KeyboardEvent.KEY_UP, onKeyUp );
  54.                 initSprite = null;
  55.             }
  56.         }
  57.        
  58.         /* return true if specified key is down
  59.          */
  60.         static public function isDown( key:uint ):Boolean
  61.         {
  62.             return keycodes[key] == true;
  63.         }
  64.  
  65.         /* return true if specified key is down
  66.          */  
  67.         static public function isDownOnce(key:uint, id:String):Boolean
  68.         {
  69.             if(keycodes[key] && keyDownUsed[key]){
  70.                 if(!keyDownUsed[key][id]){
  71.                     keyDownUsed[key][id] = true;
  72.                     return(true);
  73.                 }
  74.             }
  75.             return(false);
  76.         }
  77.  
  78.         /* return true if any keys are down
  79.          */  
  80.         static public function anyDown():Boolean
  81.         {
  82.             for each(var k:uint in keycodes)
  83.             {
  84.                 if (k) return(true);
  85.             }
  86.             return(false);
  87.         }
  88.                
  89.         /* sets a key as down
  90.          * use in company with deInit or without initing to do things such as playing back a demo
  91.          */
  92.         static public function forceDown(keyCode:int):void
  93.         {
  94.             keycodes[keyCode] = true;
  95.             if(!keyDownUsed[keyCode]) keyDownUsed[keyCode] = new Object();
  96.         }
  97.        
  98.         /* sets a key as up
  99.          * use in company with deInit or without initing to do things such as playing back a demo
  100.          */
  101.         static public function forceUp(keyCode:int):void
  102.         {
  103.             keycodes[keyCode] = false;
  104.             delete keyDownUsed[keyCode];
  105.         }
  106.        
  107.         /* clears all keys that are down
  108.          */
  109.         static public function clear():void
  110.         {
  111.             keycodes = new Array(256);
  112.             keyDownUsed = new Object();
  113.             presetKeycodes();
  114.         }      
  115.        
  116.         /* internal use
  117.          * should make the array faster as it never has to change size
  118.          */  
  119.         static private function presetKeycodes():void
  120.         {
  121.             var i:int = 256;
  122.             while (i>= 0) {
  123.                 keycodes[i] = false;
  124.                 i--;
  125.             }
  126.         }      
  127.  
  128.         /* internal use
  129.          */
  130.         static private function onKeyDown( event: KeyboardEvent ):void
  131.         {
  132.             keycodes[ event.keyCode ] = true;
  133.             if(!keyDownUsed[event.keyCode]) keyDownUsed[event.keyCode] = new Object();
  134.         }
  135.  
  136.         /* internal use
  137.          */  
  138.         static private function onKeyUp( event: KeyboardEvent ):void
  139.         {
  140.             keycodes[ event.keyCode ] = false;
  141.             delete keyDownUsed[event.keyCode];
  142.         }      
  143.  
  144.         /* a quick refernece to all keys, so you don't to use both Keys and flash.ui.Keyboard
  145.          */
  146.         public static const A:uint = 65;
  147.         public static const ALTERNATE:uint = 18;
  148.         public static const B:uint = 66;
  149.         public static const BACKQUOTE:uint = 192;
  150.         public static const BACKSLASH:uint = 220;
  151.         public static const BACKSPACE:uint = 8;
  152.         public static const C:uint = 67;
  153.         public static const CAPS_LOCK:uint = 20;
  154.         public static const COMMA:uint = 188;
  155.         public static const COMMAND:uint = 15;
  156.         public static const CONTROL:uint = 17;
  157.         public static const D:uint = 68;
  158.         public static const DELETE:uint = 46;
  159.         public static const DOWN:uint = 40;
  160.         public static const E:uint = 69;
  161.         public static const END:uint = 35;
  162.         public static const ENTER:uint = 13;
  163.         public static const EQUAL:uint = 187;
  164.         public static const ESCAPE:uint = 27;
  165.         public static const F:uint = 70;
  166.         public static const F1:uint = 112;
  167.         public static const F10:uint = 121;
  168.         public static const F11:uint = 122;
  169.         public static const F12:uint = 123;
  170.         public static const F13:uint = 124;
  171.         public static const F14:uint = 125;
  172.         public static const F15:uint = 126;
  173.         public static const F2:uint = 113;
  174.         public static const F3:uint = 114;
  175.         public static const F4:uint = 115;
  176.         public static const F5:uint = 116;
  177.         public static const F6:uint = 117;
  178.         public static const F7:uint = 118;
  179.         public static const F8:uint = 119;
  180.         public static const F9:uint = 120;
  181.         public static const G:uint = 71;
  182.         public static const H:uint = 72;
  183.         public static const HOME:uint = 36;
  184.         public static const I:uint = 73;
  185.         public static const INSERT:uint = 45;
  186.         public static const J:uint = 74;
  187.         public static const K:uint = 75;
  188.         public static const L:uint = 76;
  189.         public static const LEFT:uint = 37;
  190.         public static const LEFTBRACKET:uint = 219;
  191.         public static const M:uint = 77;
  192.         public static const MINUS:uint = 189;
  193.         public static const N:uint = 78;
  194.         public static const NUMBER_0:uint = 48;
  195.         public static const NUMBER_1:uint = 49;
  196.         public static const NUMBER_2:uint = 50;
  197.         public static const NUMBER_3:uint = 51;
  198.         public static const NUMBER_4:uint = 52;
  199.         public static const NUMBER_5:uint = 53;
  200.         public static const NUMBER_6:uint = 54;
  201.         public static const NUMBER_7:uint = 55;
  202.         public static const NUMBER_8:uint = 56;
  203.         public static const NUMBER_9:uint = 57;
  204.         public static const NUMPAD:uint = 21;
  205.         public static const NUMPAD_0:uint = 96;
  206.         public static const NUMPAD_1:uint = 97;
  207.         public static const NUMPAD_2:uint = 98;
  208.         public static const NUMPAD_3:uint = 99;
  209.         public static const NUMPAD_4:uint = 100;
  210.         public static const NUMPAD_5:uint = 101;
  211.         public static const NUMPAD_6:uint = 102;
  212.         public static const NUMPAD_7:uint = 103;
  213.         public static const NUMPAD_8:uint = 104;
  214.         public static const NUMPAD_9:uint = 105;
  215.         public static const NUMPAD_ADD:uint = 107;
  216.         public static const NUMPAD_DECIMAL:uint = 110;
  217.         public static const NUMPAD_DIVIDE:uint = 111;
  218.         public static const NUMPAD_ENTER:uint = 108;
  219.         public static const NUMPAD_MULTIPLY:uint = 106;
  220.         public static const NUMPAD_SUBTRACT:uint = 109;
  221.         public static const O:uint = 79;
  222.         public static const P:uint = 80;
  223.         public static const PAGE_DOWN:uint = 34;
  224.         public static const PAGE_UP:uint = 33;
  225.         public static const PERIOD:uint = 190;
  226.         public static const Q:uint = 81;
  227.         public static const QUOTE:uint = 222;
  228.         public static const R:uint = 82;
  229.         public static const RIGHT:uint = 39;
  230.         public static const RIGHTBRACKET:uint = 221;
  231.         public static const S:uint = 83;
  232.         public static const SEMICOLON:uint = 186;
  233.         public static const SHIFT:uint = 16;
  234.         public static const SLASH:uint = 191;
  235.         public static const SPACE:uint = 32;
  236.         public static const T:uint = 84;
  237.         public static const TAB:uint = 9;
  238.         public static const U:uint = 85;
  239.         public static const UP:uint = 38;
  240.         public static const V:uint = 86;
  241.         public static const W:uint = 87;
  242.         public static const X:uint = 88;
  243.         public static const Y:uint = 89;
  244.         public static const Z:uint = 90;
  245.        
  246.         /* experimental
  247.          */
  248.         public static const WINDOWS_KEY_LEFT:uint = 91;
  249.         public static const WINDOWS_KEY_RIGHT:uint = 92;
  250.         public static const WINDOWS_RIGHT_CLICK:uint = 93;
  251.         public static const WINDOWS_VOLUME_DOWN:uint = 174;
  252.         public static const WINDOWS_VOLUME_UP:uint = 175;
  253.         public static const WINDOWS_VOLUME_MUTE:uint = 173;
  254.         public static const WINDOWS_PLAY_PAUSE:uint = 179;
  255.         public static const WINDOWS_BACK:uint = 166;
  256.         public static const WINDOWS_FORWARD:uint = 167;
  257.         public static const NUM_LOCK:uint = 144;
  258.         public static const SCROLL_LOCK:uint = 145;
  259.         public static const PAUSE_BREAK:uint = 19;
  260.     }
  261. }

Have fun!

8 comments

  1. stereotyp

    the Key class in the casalib is also a good way to solve this problem


  2. Thanks, I'd actually not seen this lib before. Looks like some great stuff here - http://casalib.org/ if anyone else wants to check it out.

    Our class is a little different as its great to be used as a keyboard playback device. Also, the static key references as much as they might be bad programming practice are great time savers.

    I do like that CasaLib though! Thanks! :)


  3. Hi
    Vector (static with 256 size) instead of Array(dynamic) would speedup the code.
    regards


  4. Could do, but it's only FP10 compatible. :P


  5. When i pressing left and down arrows.... and If i press NUMPAD_ENTER without releasing the left and right arrows its not trigger in the swf... why this happened?... can i get the numpad_enter at left and down arrow keys still pressing?...

  6. Alex

    I must have copied the Keys.as file wrong because i get an error message saying
    (A file found in a source-path must have the same package structure ", as the definition's package, 'com.touchmypixel.io'.
    I am fairly new to Actionscript in general and would be extremely grateful if you could find a solution for this.
    Just as another suggestion, i decided to see what would happen without the use of com.touchmypixel.io
    after package in keys.as but a warning pops up pointing to the init function on line 33 saying
    overrides Object.init so I though if there was a solution to that it could be a possible alternative.
    thanks in advance, and i really appreciate the code you have written. I can imagine it will save me a lot of time if I can ever get it to work.

  7. Alex

    o haha oops! i got it to work, thank you so much for creating this. i just had to change function init to something with a different name. like i said, this is new to me. once again thanks a ton!

  8. Brandon

    Looks great, but it doesn't seem to be working for me. Probably because I'm new to AS3 ... but anyway, I copied your main code and class code into a root folder called Test5. Then when I run it, I get the error message: "The process cannot access the file 'C: ... Test 5\bin\Test5.swf' because it is being used by another process." Not sure why this is happening, but any advice would be appreciated.

 

Leave a Reply

Our Friends:

Powered by haXe / poko cms | Copyright 2008-09 TouchMyPixel