Articles Archive
Articles Search
Director Wiki
 

Arrow Keys for Game Play

May 17, 1999
by Pat McClellan

Dear Multimedia Handyman,

How do I make it so that you can move an object by pressing the Up, down, right, and left keys?

-DuK-

Dear DuK,

This is a good, simple task for learning some basics in Lingo. What you're describing involves the following:

  1. Sensing that a key was pressed and released
  2. Finding out which key it was
  3. Moving a particular sprite a certain number of pixels in the appropriate direction.

Download the sample movie in either Mac or PC format.

So, let's start with the key press. What we want to do is create a movie script (not a behavior) which will "notice" all the key presses and check them to see if it's one of the arrow keys. First, in the startMovie script, we need to assign a custom handler to the keyDownScript and another to the keyUpScript. Since I assume you're going to want the sprite to keep moving as long as the key is held down, we're going to have to monitor whether the key has been released or not. I'll use a global variable called gKeyState to keep track of that. In this startMovie script, let's initialize the gKeyState variable and set it to #released.

on startMovie
  global gKeyState
  
  set the keyDownScript to "arrows"
  set the keyUpScript to "releaseKey"
  set gKeyState = #released
  
end

The keyDownScript is set to a handler called "arrows", and the keyUpScript is set to a handler called "release". My idea here is that the keyDownScript will start the motion by setting the gKeyState to #pressed, and the keyUpScript will stop the motion by setting gKeyState to #released. Whenever gKeyState is not #released, then a message will be sent to the sprites to move.

I know that when you're playing a game like this, you want to be able to shift directions quickly -- sometimes overlapping the keypresses. So, we'll want to allow for some "slop". For example, assume that we're pressing the left arrow and the sprite is moving left, because gKeyState is #pressed. Now we quickly press the right arrow without first releasing the left arrow. The sprite starts moving right, but as soon as the left key is release, the keyUpScript ("releaseKey") will reset gKeyState to #released and the motion will stop -- even though we're still holding down the right arrow key.

In order to allow for some overlap of keys, I decided to create a third possible value for gKeyState: #secondPress. I set up an if-then-else statement in the "arrows" handler that checks gKeyState. If no key is pressed (#released), then it sets gKeyState to #pressed. Else, it sets the gKeyState to #secondPress. It also checks the keyCode, and if the keyCode is one of the arrow keys, then it sets another global variable (gKey) to #left, #right, #down, or #up. (This handler can go right below the startMovie script, in the same cast member.)

on arrows
  global gKeyState, gKey
  
  if gKeyState = #released then
    set gKeyState = #pressed
  else
    set gKeyState = #secondPress
  end if
  
  case the keyCode of
    123: set gKey = #left
    124: set gKey = #right
    125: set gKey = #down
    126: set gKey = #up
  end case
  
end

The keyUpScript handler seen below ("releaseKey") does a similar if-then-else check on the gKeyState. If gKeyState is #pressed, then we know that only one key was down (otherwise gKeyState would be #secondPress) so we set gKeyState to #released and motion will stop. The other possibility is that 2 keys had been pressed and now one had been release. That means gKeyState was #secondPress, in which case we'll set gKeyState back to #pressed. (I know all of this is a bit confusing, but you really do need it in order to have quick response that allows some overlap.)

on releaseKey
  global gKeyState, gKey
  
  if gKeyState = #pressed then
    set gKeyState = #released
  else
    set gKeyState = #pressed
  end if
  
end

So far, we haven't done anything that moves sprites. All we've done is set the gKeyState and check to see which key was pressed. Now comes the good part. The following exitFrame handler should be a movie script -- it can go in the same castmember as all the other scripts we've covered so far. Since it's a movie script, it will be executed every exitFrame of your entire movie. But, as you'll see, if the gKeyState is #released, it does literally "nothing". However, if gKeyState is either #pressed or #secondPress, then it sends a message to all sprites, calling a handler named "keyPress" and telling the sprites which key (gKey).

on exitFrame
  global gKeyState, gKey
  
  if gKeyState = #released then
    nothing
  else
    sendAllSprites(#keyPress, gKey)
  end if
  
end

That takes care of watching the key up and key down and which key part. It also sends out a message. Now we need to create a behavior which will receive the message from the exitFrame handler, and then move its sprite accordingly. Since this is for a game, I'll assume that you might want to vary the speed at which the sprites move. So we'll have a speed property, which translates to how many pixels per move the sprite will travel.

-- keyMover Behavior
-- copyright © 1999, ZZP Online, LLC
-- free use for readers of 
property pSpeed, pSprite
on getPropertyDescriptionList me
  set pdlist to [:]
  addprop pdlist, #pSpeed, [#comment:"Pixels ¬
    per move:", #format:#integer, #default:2]
  return pdlist
end getPropertyDescriptionList
on beginSprite me
  set pSprite = the spriteNum of me
end
on keyPress me, direction
  set myLocH = the locH of sprite pSprite
  set myLocV = the locV of sprite pSprite
  
  case direction of
    #left: set the locH of sprite pSprite = ¬
      myLocH - pSpeed
    #right: set the locH of sprite pSprite = ¬
      myLocH + pSpeed
    #up: set the locV of sprite pSprite = ¬
      myLocV - pSpeed
    #down: set the locV of sprite pSprite = ¬
      myLocV + pSpeed
  end case
  
end
on setSpeed me, speed
  set pSpeed = speed
end

Notice that handler called "on keypress me". That's what receives the message from the exitFrame handler. When called, it moves its sprite left, right, up or down by an increment of pSpeed pixels.

The final little handler is called "on setSpeed me". It doesn't do anything in my demo, but in your game, you can change the speed of a sprite simply by saying...

sendSprite (5, #setSpeed, 8)

In this example, I reset the speed of sprite 5 so that it now moves 8 pixels per move. These are the basics, which I hope you'll tear apart and adapt to meet your own needs.

One final note... the arrow keys are "reserved" by some browsers for browser operations, so for Shockwave, it's a better idea to use standard letter keys. In fact, my demo movie above may not work on some of your browsers for this very reason. You can try using the j, k, l, and i keys instead.

Patrick McClellan is Director Online's co-founder. Pat is Vice President, Managing Director for Jack Morton Worldwide, a global experiential marketing company. He is responsible for the San Francisco office, which helps major technology clients to develop marketing communications programs to reach enterprise and consumer audiences.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.