I never thought development of web games can be that simple until I started development of my first game with Adobe Flex. I made two games, without any prior experience with Action script, in only ten months: "Fitter" and "RGB".

I found many answers to my questions on different forums and web sites, and it is time to share my knowledge with Flex community.

Thursday, April 29, 2010

Keyboard controls

In chapter 24 it was explained how to handle keyboard events. In this chapter, we will add new functionality into existing code, that will allow player to control the snake with cursor keys.

If you wish to find out the difference between controlling the snake with cursor keys or with the mouse, try the final version of the game, embedded bellow.


KeyDown

In chapter 24 we added keyUp event handler to application, which called functions KeyUp (SnakesAdventure.as) and KeyUp_Game (Game.as), whenever any key on the keyboard was released. In case user pressed Escape key, game paused right after the key was released and program switched to GAME_MENU state.

When the player intends to change direction of the snakes motion, he would expect that direction will change right after the key was pressed, not released. That's why we have to implement new event handler, that will handle keyDown events.

New event handler is defined in file SnakesAdventure.mxml inside mx:Canvas element:

keyDown="KeyDown(event)"

Whenever any key on the keyboard is pressed, program calls KeyDown function, implemented in file SnakesAdventure.as:

private function KeyDown(event:KeyboardEvent):void
{
  switch (state)
  {
  case GAME:
    KeyDown_Game(event);
    break;
  }
}

We've used the same approach here, that was used in KeyUp. Whenever the event occurs, the event handling routine executes the appropriate KeyDown_Game, if the GAME state is active. KeyDown_Game function can be found in Game.as:

private function KeyDown_Game(event:KeyboardEvent):void
{
  switch (gameState)
  {
  case GAME_PLAYED:
    var direction:int = snake.getDirection();

    switch (event.keyCode)
    {
    case Keyboard.UP:
      if ((direction == Snake.EAST) || (direction == Snake.WEST))
        snake.setDirection(Snake.NORTH);
      break;
    case Keyboard.DOWN:
      if ((direction == Snake.EAST) || (direction == Snake.WEST))
        snake.setDirection(Snake.SOUTH);
      break;
    case Keyboard.LEFT:
      if ((direction == Snake.NORTH) || (direction == Snake.SOUTH))
        snake.setDirection(Snake.WEST);
      break;
    case Keyboard.RIGHT:
      if ((direction == Snake.NORTH) || (direction == Snake.SOUTH))
        snake.setDirection(Snake.EAST);
      break;
    }
    break;
  }
}

When the player presses any key during the game (while GAME_PLAYED state is active), function KeyDown_Game checks if it was one of cursor keys. If it was, if statement verifies if direction of motion can be changed to desired direction. For instance, player cannot change direction from north to south by pressing the down key. Only east and west directions are legal in this case.

If you download project package of the final version of the Snake's adventure, and open Game.as file, you will see that final version of KeyDown_Game function is much more complex. This is because in final version of the game, it can be activated by pressing one of the cursor keys.

If you implemented the similar keyboard event handler in your own game, you most probably noticed one problem, which are related with this new functionality.

Focus

When the game is embedded into web page, game panel does not have focus by default, which means the program will not receive events when key is pressed. Game panel is focused whenever user clicks on game panel with the mouse button. This is one of the reasons that something like "Click here..." is displayed at the beginning of almost any Flash game.

The following situation is much harder to deal with: game panel lost focus while the game is running and again, program stopped receiving key events. Situations like this happen rarely, but when they are, it can be quite frustrating for the player. One solution for situations like this, is to pause the game, and display the banner that says, for example, "Game paused, click here when you are ready".

All you have to do, is to define two event handlers in the root element (mx:Application) of mxml file. This is the example from SnakesAdventure.mxml:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    name="SnakesAdventure"
    backgroundColor="#000000"
    horizontalAlign="center"
    creationComplete="Init();"
    enterFrame="UpdateFrame();"
    activate="Activated(event);"
    deactivate="Deactivated(event);"
    paddingLeft="0"
    paddingTop="0"
    paddingBottom="0"
    paddingRight="0"
    width="480" height="480"
    preloader="Classes.PreLoader">
...

Attribute deactivate creates new event handler, that executes Deactivated function. deactivate events occur whenever player clicks outside the game panel, browser is minimized, new window or tab becomes active... Attribute activate creates event handler, that executes Activated function. Event is dispatched when application becomes active and has focus. You can find example of both functions in file SnakeAdventures.as.