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.

Wednesday, February 3, 2010

Stage one

We are not ready to begin development of the first stage of the game, yet. In this chapter we will create two labels, which will be displayed at the beginning of each stage. One will show the number of current stage at the top and second one "Press mouse button when ready" at the bottom. We will also add some code, that will come in handy later, when we start working on gameplay.

I add two new images in the project that will be used in the implementation, both images have five different levels of opacity (from 0.2 to 1.0). Images can be found in Images folder in project package for this chapter.

First image will be used to display "Stage XX" label, XX is number of current stage. Image contains Stage string and digits from 0 to 9. With those ten digits we can display all natural numbers, but we only need to display numbers from 01 to the number of last stage available in the game. Stage sign will be copied into screen buffer in three steps:

- stage text
- integer number of current stage divided by 10
- remainder of current stage divided by 10

Transparent parts of the image will be used to implement smooth transition between visible and hidden sign (and vice versa).

 

Second image will be used to display the blinking text "Press mouse button when ready". Transparent parts of the image will be used to implement smooth transition between visible and hidden label (and vice versa).

 

Labels will be displayed as long as mouse button is not pressed. In previous chapter we implemented game state automaton and defined four states. Sign will become visible in GAME_FADE_IN state, GAME_START state will be active as long as mouse button is not pressed. When mouse button is pressed, GAME_STARTED state becomes active, and text start to slowly disappear.

OK, let's start coding. Open Game.as and add definition of two variables to other variable definitions:

private var newState:int = 0;

private var level:int = 0;

Usage of newState variable will be explained later, level variable will contain the number of active stage/level. We will set this variable to 1 when player select new game option in game menu. Add this line at the beginning of New_Game function:

level = 1;

Implementation of Draw_Game function will require a little bit more work and explanation. Add this piece of code to Draw_Game:

gameTime += elapsedTime;

screenBuffer.fillRect(new Rectangle(0,0,SCREEN_WIDTH,SCREEN_HEIGHT),0xFF406020);

In the first line we count the time (in second) since the current game state become active. Value is increased by time interval between previous and current frame. The fillRect method paints whole area of screen buffer in green colour. This is just temporary, we will remove this line when we start implementing scrolling background. Until then, green background will serve us better than default black background.

Function Draw_StageSign

Draw_StageSign function will be used to display both signs, implementation is quite long, but don't let it scare you off, there is nothing you haven't seen before, if you followed this tutorial from the beginning. Add Draw_StageSign function to Game.as file:

private var stageLabelBitmap:BitmapData = null;
private var stageReadyBitmap:BitmapData = null;

private function Draw_StageSign(stageOpacity:Number,readyOpacity:Number):void
{
  if (stageLabelBitmap == null)
  {
    stageLabelBitmap = new BitmapData(350,160,true,0x00000000);
    stageLabelBitmap.draw(new stageLabelImg());

    stageReadyBitmap = new BitmapData(240,250,true,0x00000000);
    stageReadyBitmap.draw(new stageReadyImg());
  }

  if (stageOpacity > 0.15)
  {
    var yPos:int = 0;

    if (stageOpacity < 0.25)
      yPos = 128;
    else
    if (stageOpacity < 0.45)
      yPos = 96;
    else
    if (stageOpacity < 0.45)
      yPos = 64;
    else
    if (stageOpacity < 0.55)
      yPos = 32;

    screenBuffer.copyPixels(stageLabelBitmap,
                            new Rectangle(0, yPos, 100, 32),
                            new Point(SCREEN_WIDTH / 2 - 80,SCREEN_HEIGHT / 2 - 120));

    screenBuffer.copyPixels(stageLabelBitmap,
                            new Rectangle(int(level / 10) * 24 + 102, yPos, 24, 32),
                            new Point(SCREEN_WIDTH / 2 + 30,SCREEN_HEIGHT / 2 - 120));

    screenBuffer.copyPixels(stageLabelBitmap,
                            new Rectangle((level % 10) * 24 + 102, yPos, 24, 32),
                            new Point(SCREEN_WIDTH / 2 + 54,SCREEN_HEIGHT / 2 - 120));
  }

  if (readyOpacity > 0.15)
  {
    var yPos2:int = 0;

    if (readyOpacity < 0.25)
      yPos2 = 200;
    else
    if (readyOpacity < 0.45)
      yPos2 = 150;
    else
    if (readyOpacity < 0.45)
      yPos2 = 100;
    else
    if (readyOpacity < 0.55)
      yPos2 = 50;

    screenBuffer.copyPixels(stageReadyBitmap,
                            new Rectangle(0, yPos2, 240, 50),
                            new Point(SCREEN_WIDTH / 2 - 120,SCREEN_HEIGHT / 2 - 50));
  }
}

Function requires two parameters, first parameter is opacity of "Stage XX" label, second parameter is opacity of "Press mouse button when ready" label. When the function is executed first time, both BitmapData objects are created from embedded images. Object stageLabelBitmap contains content of StageLabel.png, stageReadyBitmap contains content of StageReady.png.

Second block of code executes only when value of stageOpacity is greater than 0.15, otherwise "Stage XX" label is not displayed. Variable yPos contains the value of vertical position in source image. Values are a little bit modified according to opacity of labels in source image. Height of each label in the source image is 32, that is why the values 0, 32, 64, 96 or 128 are set to yPos.

First copyPixel function copies part of source image that contains Stage label, width of the rectangle is hundred pixels. Top left pixel of the source image is copied 80 pixels to the left and 120 above the centre of screen buffer.

Both numbers are copied at the same vertical position, horizontal position is set on the right side of screen buffer, that is why complete label appears horizontally centred. Digits in the source image start at column 102, width of each digit is 24 pixels. To display digit 0, we set horizontal position in source image to 102 + 24 * 0 = 102, to display digit 4 we use value 102 + 24 * 4 = 192. Value of both digits is calculated from level variable. First digit is result of division of level by 10, second digit is reminder of same division.

Last block of code displays "Press mouse button when ready" text with level of opacity required by readyOpacity parameter. We use same approach that we used to display "Stage" text, except this time we use different source image, where labels are 50 pixels apart (vertically).

Press mouse button when ready

When mouse button is pressed, GAME_START state ends and GAME_STARTED state becomes active and both labels start to disappear. Because the "Press mouse button when ready" label is blinking during the GAME_START state, we have to wait until label is fully opaque, before we change the state. We will store value of new state into newState variable when mouse button is pressed and Draw_Game will change the state, when time is right. Replace the current version MouseDown_Game function with this one:

private function MouseDown_Game(event:MouseEvent):void
{
  var mx:int = event.localX;
  var my:int = event.localY;

  switch (gameState)
  {
  case GAME_START:
    newState = GAME_STARTED;
    break;
  }
}

Function Draw_Game part II

Now, that we have Draw_StageSign function implemented, it's time that we put it in use. Draw_StageSign will be called only when game states GAME_FADE_IN, GAME_START and GAME_STARTED are active. Put the following switch block at the end of Draw_Game function:

switch (gameState)
{
case GAME_FADE_IN:
  Draw_StageSign(gameTime,gameTime);

  Apply_FadeEffect(1 - gameTime);

  if (gameTime >= 1)
    ChangeState_Game(GAME_START);
  break;
case GAME_START:
  var fracTime:Number = gameTime - int(gameTime);

  Draw_StageSign(1,int(gameTime + 1) % 2 == 0 ? fracTime : 1 - fracTime);

  if ((newState == GAME_STARTED) &&
     (int(gameTime + 1) % 2 == 0 ? fracTime > 0.55 : fracTime < 0.55))
  {
    ChangeState_Game(GAME_STARTED);
    newState = 0;
  }
  break;
case GAME_STARTED:
  Draw_StageSign(1 - gameTime,1 - gameTime);

  if (gameTime >= 1)
    ChangeState_Game(GAME_PLAYED);
  break;
}

GAME_FADE_IN state is active for one second. Within this second, both labels start to appear, but it is hard to notice the effect, because fade-in effect is applied over complete screen buffer. Values for Draw_StageSign parameters can range from 0.0 (not visible) to 1.0 (visible). After one second, labels are fully visible and GAME_START state becomes active.

GAME_START state is active until mouse button is pressed. Value of gameTime variable increases over time, variable fracTime has the fractional part of gameTime value. For example when value of gameTime is 3.75 (seconds) value of fracTime is 0.75.

During GAME_START state, Stage label is visible all the time, first parameter in Draw_StageSign function call is set to 1. On the other hand "Press mouse button..." label is blinking all the time. On the odd seconds (integer value of gameTime variable is odd) label slowly disappears, on the even seconds it is becoming visible again.

if statement checks whether newState variable is set (mouse button was pressed) and when blinking label becomes fully visible, changes the state to GAME_STARTED. This state is active for one second, during this time both labels slowly disappear.





You can set different values to level variable in New_Game function, to see how those number look like. Just don't forget to set the value back to 1, when you are done.

No comments:

Post a Comment