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.

Sunday, February 14, 2010

Custom mouse cursors

In Flex applications, system arrow cursor is displayed by default. Developers can use other icons defined in MouseCursor class, like hourglass, hand... Icons look fine when user interface is made from Flex UI components, but are a bit to boring for games and usually don't fit with the games graphics at all.

So far, we didn't pay any attention to mouse cursor, that is why only default arrow icon was displayed in the game. In this chapter we will embed nine new images into the game and use them to display mouse cursor. One image will be used for the mouse cursor in the game menu, the rest will be set according to location of mouse cursor and the snake. New images can be found inside Images folder in project package for this chapter.

There are two ways to implement custom mouse cursors in the Flex applications:
- hiding mouse cursor with Mouse.hide() and drawing new image, part of the image or animation, on the location of mouse cursor
- replacing active mouse cursor with new image by using functions from CursorManager class.

We will use the second approach to learn how to use CustomManager, although first approach is more flexible. You can see how new mouse cursors look if you start the game bellow. If the snake runs out of the screen, reload the page to restart the game.




CursorManager

Adobe® Flex Language Reference: The CursorManager class controls a prioritized list of cursors, where the cursor with the highest priority is currently visible. If the cursor list contains more than one cursor with the same priority, the Cursor Manager displays the most recently created cursor.

Before we can use CursorManager, we have to import it. Add new import statement into SnakesAdventure.as:

import mx.managers.CursorManager;

CursorManager class contains fifteen public functions but we will use only two: removeCursor and setCursor. We will implement all the functionality that removes active cursor and set new one, in SetMouseCursor function in file SnakesAdventure.as:

private var cursorID:Number = 0;
private var mouseCursorDirection:int = -1;
private var mouseCursorDisabled:Boolean = false;

private function SetMouseCursor(direction:int,disabled:Boolean):void
{
  if ((mouseCursorDirection != direction) || (mouseCursorDisabled != disabled))
  {
    mouseCursorDirection = direction;
    mouseCursorDisabled = disabled;

    CursorManager.removeCursor(cursorID);

    switch (direction)
    {
    case 0:
      cursorID = CursorManager.setCursor(mouseCursorImg);
      break;
    case Snake.NORTH:
      if (disabled)
        cursorID = CursorManager.setCursor(mouseCursorNDImg);
      else
        cursorID = CursorManager.setCursor(mouseCursorNImg);
      break;
    case Snake.EAST:
      if (disabled)
        cursorID = CursorManager.setCursor(mouseCursorEDImg);
      else
      cursorID = CursorManager.setCursor(mouseCursorEImg);
      break;
    case Snake.SOUTH:
      if (disabled)
        cursorID = CursorManager.setCursor(mouseCursorSDImg);
      else
        cursorID = CursorManager.setCursor(mouseCursorSImg);
      break;
    case Snake.WEST:
      if (disabled)
        cursorID = CursorManager.setCursor(mouseCursorWDImg);
      else
        cursorID = CursorManager.setCursor(mouseCursorWImg);
      break;
    }
  }
}

SetMouseCursor function requires two parameters, cursor image is selected according to value of both parameters. Zero value of direction parameter sets game menu cursor. If value equals NORTH, EAST, SOUTH or WEST constant from Snake class, function picks the image with arrow that points in the same direction. Parameter disabled controls if grey (true) or coloured (false) version of image is used.

Two global variables: mouseCursorDirection and mouseCursorDisabled, make sure that mouse cursor is not replaced, if values of both parameters define the same mouse cursor that is currently active. Values are stored whenever mouse cursor icon is changed and compared with values of direction/disabled the next time function is called. If both pair of values are equal, the whole process is skipped.

Before we can use SetMouseCursor function, we have to embed new cursor images into the application. Add this code into Images.as file:

[Embed(source="Images/MouseCursor.png")]private var mouseCursorImg:Class;
[Embed(source="Images/MouseCursor_N.png")]private var mouseCursorNImg:Class;
[Embed(source="Images/MouseCursor_ND.png")]private var mouseCursorNDImg:Class;
[Embed(source="Images/MouseCursor_E.png")]private var mouseCursorEImg:Class;
[Embed(source="Images/MouseCursor_ED.png")]private var mouseCursorEDImg:Class;
[Embed(source="Images/MouseCursor_S.png")]private var mouseCursorSImg:Class;
[Embed(source="Images/MouseCursor_SD.png")]private var mouseCursorSDImg:Class;
[Embed(source="Images/MouseCursor_W.png")]private var mouseCursorWImg:Class;
[Embed(source="Images/MouseCursor_WD.png")]private var mouseCursorWDImg:Class;

We embedded all required images and implemented SetMouseCursor function, now we can use it to get rid of system arrow cursor.

Game menu cursor

We can set new mouse cursor at the start of the program. Init function is called right after Flex finishes its part of program initialization procedure. Let's set the game menu cursor at the beginning of Init function:

SetMouseCursor(0,false);

If you compile and run the game at this point, you can see, that new cursor is displayed only when it is positioned inside game frame. Outside the frame cursor icon is set back to default.

Game cursor

Cursor in the game is set according to its position and position of the head of the snake. We will implement all functionality inside SetGameCursor function, this time without any parameters. Function will be used and implemented inside Game.as file. Add call to SetGameCursor at the end of Draw_Game function:

SetGameCursor();

We will use the same approach here that we did in MouseDown_Game when mouse button was pressed during the game. Direction, that mouse cursor arrow will point to, is calculated from mouse cursor position and position of the snake. Disabled cursor (geay icon) is set, when snake movement cannot change in that direction. For instance, when snake is moving in the northern direction, north and south direction cannot be set.

private function SetGameCursor():void
{
  var mx:int = mouseX;
  var my:int = mouseY;

  my = SCREEN_HEIGHT - my;
  var position:Point = snake.getPosition();
  var direction:int = snake.getDirection();
  var cursorDirection:int = 0;
  var cursorDisabled:Boolean = true;

  if (Math.abs(mx - position.x) > Math.abs(my - position.y))
  { //check east/west
    if (mx - position.x > 0)
      cursorDirection = Snake.EAST;
    else
      cursorDirection = Snake.WEST;

    if (Math.abs(mx - position.x) > Snake.SPEED)
      if ((direction == Snake.NORTH) || (direction == Snake.SOUTH))
        cursorDisabled = false;
  }
  else
  { //check south/north
    if (my - position.y > 0)
      cursorDirection = Snake.NORTH;
    else
      cursorDirection = Snake.SOUTH;

    if (Math.abs(my - position.y) > Snake.SPEED)
      if ((direction == Snake.EAST) || (direction == Snake.WEST))
        cursorDisabled = false;
  }

  SetMouseCursor(cursorDirection,cursorDisabled);
}

We cannot obtain position of mouse cursor from MouseEvent object as we did in MouseUp/Down/Moved functions. Current position of mouse cursor is obtained from mouseX and mouseY properties of Application class.

Value of cursorDirection variable is set according to difference between mouse cursor position and position of the head of the snake. If difference between y values is positive and greater that difference between x, value is set to NORTH. If difference between y values is negative but absolute value is greater that absolute value of difference between x, value is set to SOUTH. And so on. Value of cursorDisabled is set to true by default, and set to false only if direction of snake movement can change to cursorDirection.

At the end, SetMouseCursor function implemented in SnakesAdventure.as, is called.

No comments:

Post a Comment