Flappy Bird in Excel VBA Part 12 - Managing Game State
This part of the tutorial implements a state system to make it easier to determine what actions to perform each time the game updates.

Posted by Andrew Gould on 24 April 2014

You need a minimum screen resolution of about 700 pixels width to see our blogs. This is because they contain diagrams and tables which would not be viewable easily on a mobile phone or small laptop. Please use a larger tablet, notebook or desktop computer, or change your screen resolution settings.

Flappy Bird in Excel VBA - Managing Game State

Useful Links

Return to the Flappy Bird in Excel VBA Tutorial index.

Download Flappy Owl Pt12 - Game State.

Introduction

The main game update method is becoming more complex as we add more code. A neat way to help the game decide what to do each time it updates is to give it a state. Each time the game is updated it can check what the current state is and take the appropriate action.

Creating the GameState Enumeration

Go to the modPublicDeclarations module and add the following enumeration to the bottom:

Public Enum GameState

gsStopped = 0

gsPlaying = 1

gsDead = 2

gsPaused = 3

End Enum

We don't actually need to assign the numbers to each element as these would be the default values anyway. We may use more or fewer states than we've declared here but we can always add or delete values later on.

Creating the State Variable

Now return to the modGameCode module and add a variable at the top.

Private State As GameState

This variable will be initialised with a value of 0 which corresponds to the gsStopped element of the enumeration.

Changing the State Variable

When the game begins we need to change the state of the game to gsPlaying. Go to the InitialiseGame subroutine and add this line to the end:

State = GameState.gsPlaying

End Sub

Our other state changes will take place within the UpdateAndDrawGame subroutine. Edit the code so that it looks like this:

Public Sub UpdateAndDrawGame()

'Called by the SetTimer function

'Runs once for each tick of the timer clock

'Updates all game logic

'Draws all game objects

If GetAsyncKeyState(vbKeyTab) <> 0 Then

State = GameState.gsStopped

End If

Bird.Update

Wall.Update

'test for collisions

If Bird.Colliding(Wall.TopWallRange, Wall.BottomWallRange) Then

State = GameState.gsDead

End If

Bird.Draw

Wall.Draw

End Sub

We've removed the lines which immediately terminate the game in favour of instructions to simply change the state of the game. Now we need to test what the state is each time the game is updated.

Testing the Game State

We'll use a case statement to determine the game's state. All of the code that we currently have in the UpdateAndDrawGame subroutine needs to happen while the game state is gsPlaying. If the game state is set to gsStopped then we'll simply quit straight to the game menu. Change the procedure so that it looks like this:

Public Sub UpdateAndDrawGame()

'Called by the SetTimer function

'Runs once for each tick of the timer clock

'Updates all game logic

'Draws all game objects

Select Case State

'do all of this while the game is playing

Case GameState.gsPlaying

If GetAsyncKeyState(vbKeyTab) <> 0 Then

State = GameState.gsStopped

End If

Bird.Update

Wall.Update

'test for collisions

If Bird.Colliding(Wall.TopWallRange, _

Wall.BottomWallRange) Then

State = GameState.gsDead

End If

Bird.Draw

Wall.Draw

'when the game is stopped exit the game

Case GameState.gsStopped

TerminateGame

'when the player dies...

Case GameState.gsDead

End Select

End Sub

Technically you could run the game at this point - if you collide into a wall the game will simply stop and do nothing. You can't even press TAB to quit because the game only tests for this key being pressed if the game state is gsPlaying.

Resetting the Game When the Player Dies

When the player dies we'd like to give the player a choice of whether to start again or quit to the game menu. If they choose to start again we'll need some way to reset the game to its starting position. We'll get to the stage where we can offer the player a choice in a later part of the tutorial, for now we'll focus on how the make the game restart automatically.

To start with we'll create a new method in the clsGameSheet class module. Go there now and add the following subroutine:

Public Sub Reset()

GameRange.Interior.Color = GameColour.gcSkyBlue

End Sub

Next, we'll create a reset method in the clsBird class module. Add the following subroutine to this module:

Public Sub Reset()

Set BirdCell = _

pGameSheet.GameRange.Cells(Int(pGameSheet.GameHeight / 2), 41)

BirdImage.Copy BirdCell

BirdVerticalMovement = 0

Set BirdCurrentRectangle = _

Range(BirdCell, BirdCell.Offset(BirdHeight - 1, BirdWidth - 1))

PreviousUpKeyState = GetAsyncKeyState(vbKeyUp)

PreviousDownKeyState = GetAsyncKeyState(vbKeyDown)

End Sub

We'll add one more reset method, this time in the clsWall class module. Go there now and add the following subroutine:

Public Sub Reset()

CreateNewWall

WallTopImage.Copy WallTopCell

WallBottomImage.Copy WallBottomCell

End Sub

Now go back to the modGameCode module and add a new subroutine at the bottom:

Public Sub ResetGame()

'pause before restarting

Sleep 500

'reset game objects

GameSheet.Reset

Bird.Reset

Wall.Reset

'set game state to playing

State = GameState.gsPlaying

End Sub

 

Finally, return to the UpdateAndDrawGame subroutine and call the ResetGame procedure in the appropriate part of the case statement:

'when the player dies...

Case GameState.gsDead

ResetGame

End Select

End Sub

Testing the Restart Feature

At this point you can run the game and see what happens if the bird collides with a wall. You should find that the game endlessly restarts until you press TAB to quit to the game menu.

Choosing to Restart or Quit

Rather than automatically restarting the game whenever the player dies we'd like to offer them the choice of restarting or quitting to the menu. In a later part of the tutorial you'll see how to get the game to print messages in a custom font that we'll design. For now we'll simply add code that will check if the player presses y or n after the bird collides with a wall. Pressing y means that the player wants to start again, while pressing n means that they want to quit to the menu.

We'll start by making sure that the y and n keys have been deactivated when the game starts. In the modGameCode module add these four lines to the SetGameKeys subroutine:

Application.OnKey "y", ""

Application.OnKey "n", ""

Application.OnKey "Y", ""

Application.OnKey "N", ""

The OnKey method treats lowercase and uppercase letters as separate keys so we should make sure to deactivate both versions of each letter. We should also make sure that we reset these keys when the game ends. Add the following four lines to the ResetKeys subroutine:

Application.OnKey "y"

Application.OnKey "n"

Application.OnKey "Y"

Application.OnKey "N"

In a later part of the tutorial we'll see a much better way to enable and disable keys by looping over a range of cells containing the names of the keys we want to process.

Now return to the UpdateAndDrawGame subroutine and change the part of the case statement which deals with the gsDead game state:

'when the player dies...

Case GameState.gsDead

If GetAsyncKeyState(vbKeyY) <> 0 Then

ResetGame

ElseIf GetAsyncKeyState(vbKeyN) <> 0 Then

TerminateGame

End If

End Select

End Sub

Finally, go to the ResetGame subroutine and delete the line which says Sleep 500. The final subroutine should look like this:

Public Sub ResetGame()

'reset game objects

GameSheet.Reset

Bird.Reset

Wall.Reset

'set game state to playing

State = GameState.gsPlaying

End Sub

Testing the Game

You should now be able to run the game and choose what to do if the bird crashes into a wall. Press y to start again or n to quit to the game menu. As always, if you find that your code doesn't work you can download the working example from the top of the page.

What's Next

Our project now has most of the main components of a regular game but the one thing that it's missing is sound. The next part of the tutorial shows you how to use Windows API functions to play sounds during the game.

This blog has 0 threads Add post