Flappy Bird in Excel VBA Part 4 - The Game Timing Loop
This part of the tutorial describes how to set up a timing loop which allows the game to update continuously.

Posted by Andrew Gould on 11 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 - The Game Timing Loop

Useful Links

Return to the Flappy Bird in Excel VBA Tutorial index.

Download Flappy Owl Pt4 - Main Game Loop.

Introduction

This part of the tutorial introduces some extra Windows API functions and explains how to set up the sequence of individual procedures that will form our main game loop.

The SetTimer and KillTimer Functions

The function that will control our main game loop is called SetTimer. Copy the following declaration into your modPublicDeclarations module.

Public Declare Function SetTimer Lib "user32" ( _

ByVal hWnd As Long, _

ByVal nIDEvent As Long, _

ByVal uElapse As Long, _

ByVal lpTimerFunc As Long) As Long

If you're using a 64-bit edition of Office 2010 or later you'll need this version of the declaration:

Public Declare PtrSafe Function SetTimer Lib "user32" ( _

ByVal hWnd As LongPtr, _

ByVal nIDEvent As LongPtr, _

ByVal uElapse As Long, _

ByVal lpTimerFunc As LongPtr) As LongPtr

This function starts a timer which calls a specific procedure at a specified interval. The two parameters which interest us are uElapse, which is the number of milliseconds between each call to the procedure, and lpTimerFunction, which is a reference to the procedure that we want to call.

As we'll need to stop the timer when the game ends we should declare the KillTimer function as well. Copy this into the same module:

Public Declare Function KillTimer Lib "user32" ( _

ByVal hWnd As Long, _

ByVal nIDEvent As Long) As Long

The 64-bit version of this declaration is shown below:

Public Declare PtrSafe Function KillTimer Lib "user32" ( _

ByVal hWnd As LongPtr, _

ByVal nIDEvent As LongPtr) As Long

If you're using the version-independent declarations for your Windows API functions your entire modPublicDeclarations module should now look like this:

Option Explicit

#If Win64 Then

'Code is running in 64-bit Office

Public Declare PtrSafe Sub Sleep Lib "kernel32" ( _

ByVal dwMilliseconds As Long)

Public Declare PtrSafe Function SetTimer Lib "user32" ( _

ByVal hwnd As LongPtr, _

ByVal nIDEvent As LongPtr, _

ByVal uElapse As Long, _

ByVal lpTimerFunc As LongPtr) As LongPtr

Public Declare PtrSafe Function KillTimer Lib "user32" ( _

ByVal hwnd As LongPtr, _

ByVal nIDEvent As LongPtr) As Long

#Else

'Code is running in 32-bit Office

Public Declare Sub Sleep Lib "kernel32" ( _

ByVal dwMilliseconds As Long)

Public Declare Function SetTimer Lib "user32" ( _

ByVal hwnd As Long, _

ByVal nIDEvent As Long, _

ByVal uElapse As Long, _

ByVal lpTimerFunc As Long) As Long

Public Declare Function KillTimer Lib "user32" ( _

ByVal hwnd As Long, _

ByVal nIDEvent As Long) As Long

#End If

Adding the Basic Game Procedures

When writing complex systems it pays to be organised as early as possible in the process. We're going to create a separate module to hold our game code, as well as separate modules for the main objects involved in the game. Ordinarily I'd use class modules to do this, but we'll stick to normal modules for now and look at how to switch to class modules later in the tutorial.

Start by inserting a new module called modGameCode. When you've done that, write or copy the following code into it:

Option Explicit

Public Sub InitialiseGame()

'Called once when game first starts

'Used to set starting parameters

'Begins the game timer

shTest.Select

Range("A1").Select

Cells.Clear

End Sub

Public Sub TerminateGame()

'Called once when game ends

'Used to tidy up

End Sub

Public Sub UpdateAndDrawGame()

'Called by the SetTimer function

'Runs once for each tick of the game clock

'Updates all game logic

'Draws all game objects

End Sub

The comments in each procedure describe reasonably neatly what each procedure is going to do. The InitialiseGame procedure will be called when we start the game, eventually this will be done by clicking a button on the worksheet. This procedure also sets the game timer running. The game timer will call the UpdateAndDrawGame procedure continuously until something in our game logic tells it to stop. At that point we'll call the TerminateGame procedure which will stop the timer and reset the game.

Creating the Timer Procedures

The next job is to create two timer procedures, one to start the game timer and one to stop it. Insert a new module called modTimerCode and add the following code to it:

Option Explicit

'stores the ID of the timer created

Private GameTimerID As Long

Public Sub InitialiseTimer()

'the time in milliseconds between each tick

Dim GameTimerInterval As Double

'makes the game clock tick 20 times per second

GameTimerInterval = 50

'pauses for half a second before starting game

Sleep (500)

'starts the timer calling UpdateGame

GameTimerID = _

SetTimer(0, 0, GameTimerInterval, AddressOf UpdateAndDrawGame)

End Sub

Public Sub TerminateTimer()

If GameTimerID <> 0 Then

'stops the timer whose ID we stored earlier

KillTimer 0, GameTimerID

GameTimerID = 0

End If

End Sub

The comments should describe what's happening here reasonably well. The GameTimerID variable is used to remember which timer was started by the game. It's declared as private because it's only used in this module.

We set the GameTimerInterval variable to 50, meaning that the game will update every 50 milliseconds, or 20 times per second. We could just as easily have used an expression here, for example:

'makes the game clock tick 60 times per second

GameTimerInterval = 1000 / 60

In most real games the game updates at either 30 times per second or 60 times per second. For our rudimentary Excel game this isn't quite so important so you'll be able to play with this value later to speed up or slow down the game. Leave it set to 50 for now.

The most important line is the one which calls the SetTimer function. We pass in the value of our timer interval and tell it to call our UpdateAndDrawGame procedure. The AddressOf operator is used to pass a pointer to that procedure into the SetTimer function. Windows API functions don't call procedures in quite the same way as normal VBA procedures do so the AddressOf operator is important here.

Starting and Stopping the Timer

Now we can go back to the modGameCode module by double-clicking on its name in the Project Explorer. To start the game timer we simply need to add this line to the end of the InitialiseGame subroutine:

InitialiseTimer

End Sub

We should also add this line to the end of the TerminateGame procedure:

TerminateTimer

End Sub

The next thing we need to do is make the game actually do something when it runs!

Creating a Module for the Bird Code

Just as we've created separate modules to store code for the game and timer, we'll have a separate module for the bird called modBirdCode. Create it and then add the following code to it:

Option Explicit

Private BirdCell As Range

Private BirdPreviousCell As Range

Private BirdVerticalMovement As Long

Private Const Gravity As Byte = 1

Private FloorRange As Range

Public Sub InitialiseBird()

'set initial bird parameters

Set BirdCell = shTest.Range("R5")

BirdVerticalMovement = 0

BirdCell.Interior.Color = rgbCornflowerBlue

Set FloorRange = shTest.Range("A40:Z40")

FloorRange.Interior.Color = rgbBlack

End Sub

Public Sub UpdateBird()

'calculate how many cells to fall

BirdVerticalMovement = BirdVerticalMovement + Gravity

'remember the cell that the bird was in previously

Set BirdPreviousCell = BirdCell

'store the new destination cell

Set BirdCell = BirdCell.Offset(BirdVerticalMovement, 0)

'check if the new destination is past the floor

If BirdCell.Row >= FloorRange.Row Then

'if so, put the bird above the floor

Set BirdCell = Cells(FloorRange.Row - 1, BirdCell.Column)

'reverse the movement direction (makes the bird bounce)

BirdVerticalMovement = -8

End If

End Sub

Public Sub DrawBird()

'clear the old cell, colour in the new cell

BirdPreviousCell.Clear

BirdCell.Interior.Color = rgbCornflowerBlue

End Sub

 

Each of these subroutines will need to be called in the appropriate place from the modGameCode module, so head back there next. Add a line to the InitialiseGame procedure so that it looks like this:

Public Sub InitialiseGame()

'Called once when game first starts

'Used to set starting parameters

'Begins the game timer

shTest.Select

Range("A1").Select

Cells.Clear

InitialiseBird

InitialiseTimer

End Sub

Now add two lines to the UpdateAndDrawGame 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

UpdateBird

DrawBird

End Sub

What's Next

That's it for our basic game logic. When we finally start the game it will continue to run through the UpdateAndDrawGame procedure until the timer is stopped. Now all we have to do is provide a way for the player to start and stop the game! The next part of the tutorial describes how to do that.

This blog has 0 threads Add post