Read our blogs, tips and tutorials
Try our exercises or test your skills
Watch our tutorial videos or shorts
Take a self-paced course
Read our recent newsletters
License our courseware
Book expert consultancy
Buy our publications
Get help in using our site
538 attributed reviews in the last 3 years
Refreshingly small course sizes
Outstandingly good courseware
Whizzy online classrooms
Wise Owl trainers only (no freelancers)
Almost no cancellations
We have genuine integrity
We invoice after training
Review 30+ years of Wise Owl
View our top 100 clients
Search our website
We also send out useful tips in a monthly email newsletter ...
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. |
In this blog
Return to the Flappy Bird in Excel VBA Tutorial index.
Download Flappy Owl Pt12 - Game State.
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.
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.
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.
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.
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.
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
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.
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
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.
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.
Some other pages relevant to the above blog include:
Kingsmoor House
Railway Street
GLOSSOP
SK13 2AA
Landmark Offices
99 Bishopsgate
LONDON
EC2M 3XD
Holiday Inn
25 Aytoun Street
MANCHESTER
M1 3AE
© Wise Owl Business Solutions Ltd 2024. All Rights Reserved.