# VBForums CodeBank > Codebank - Game Programming >  [Vb.Net] Managed Game Loop

## dday9

In this thread I will discuss how to create a managed game loop in a proper manner. First I will identify ways that are most commonly used and explain why they should not be used. Then I will explain how to set up the proper managed game loop as well as why it works the way it does.

In game programming, one of the biggest and most basic concepts that should be applied is the game loop. A game loop is simply something that keeps the game in perpetual motion. Think of a normal windows form application for a second, in most scenarios the program remains idle until a user invokes some sort of an event such as clicking a button. In game programming, the game is constantly moving. In other words the game assumes that something is always happening and doesn't wait for user interaction for the program to continue.

Something that most beginners use as their game loop is the System.Windows.Forms.Timer class. This is OK in the sense that the timer does provide a simple means of raising an event(the Tick event) at user-defined intervals, however these timers are notoriously unreliable at lower intervals. The Interval property of the Timer gets or sets the time measured in milliseconds that invokes the Tick event. Therefore the lower the Interval, the less time between ticks. While theoretically the timer's Interval can be at one, realistically the timer starts to become inaccurate when it's Interval is set at about 50. The reason for this lies in the fact that this timer is not a high precision timer. This translates to over a period of time being off x amount of milliseconds(which is sufficient for most applications) after each Tick, there starts to be greater period of gaps. In games, those gaps are what are known as lag.

So if using the System.Windows.Forms.Timer is not a suitable solution, then why not set up a loop that runs as fast as the CPU will allow it to run. Running this type of loop is what is known as running a "Busy Wait" or "Busy Loop". The technique is often used(incorrectly) in normal windows form applications to just kill time until something has happened. One of the reasons why this is a terrible solution is because it kills the performance of the computer and can raise the CPU usage to almost %100! The other reason for this not being a suitable solution is the concept of a locked frame rate. The frame rate is the speed at which the game is refreshed measured in frames-per-second(FPS). Having a locked frame rate allows for a consistent animation of the game. Using a busy loop does not produce a precise and predictable frame rates which result in choppy animation.

The busy loop and the System.Windows.Forms.Timer is out of the question for our game loop. So what do we use? From my experience there are two solutions. One, which is the simpler of the two(but less accurate), is using a high precision timer. In the .NET framework you can find a high precision timer in the System.Diagnostics.Stopwatch class. This class, unlike the System.Windows.Forms.Timer class, does not provide a Tick event or some equivalent; so it is our job to create a method that will keep track of the frame rate and lock in the frames-per-second. This should be done in a separate thread to achieve maximum accuracy. The second solution is to use the QueryPerformance APIs. The QueryPerformance API's consist of two APIs: the QueryPerformanceCounter function and the QueryPerformanceFrequency function. Using both of these functions will return a high-resolution time stamp that cannot be surpassed.

Here is an example of using the System.Diagnostics.Stopwatch:


```
Public Class GameLoop
    Inherits ComponentModel.Component

    Private _enabled As Boolean
    Public Property Enabled As Boolean
        Get
            Return _enabled
        End Get
        Set(ByVal value As Boolean)
            If Not _enabled.Equals(value) Then
                _enabled = value
                Me.OnEnabledChanged()
            End If
        End Set
    End Property

    Private _interval As Single
    Public Property Interval As Single
        Get
            Return _interval
        End Get
        Set(ByVal value As Single)
            If Not _interval.Equals(value) Then
                _interval = value
                Me.OnIntervalChanged()
            End If
        End Set
    End Property

    Protected Overridable Sub OnEnabledChanged()
        RaiseEvent EnabledChanged(Me, EventArgs.Empty)
    End Sub

    Protected Overridable Sub OnIntervalChanged()
        RaiseEvent IntervalChanged(Me, EventArgs.Empty)
    End Sub

    Public Event EnabledChanged(ByVal sender As Object, ByVal e As EventArgs)

    Public Event IntervalChanged(ByVal sender As Object, ByVal e As EventArgs)

    Public Event Tick(ByVal sender As Object, ByVal e As GameLoopEventArgs)

    Private Sub GameLoop_EnabledChanged(sender As Object, e As EventArgs) Handles Me.EnabledChanged
        If _enabled Then
            Dim game_thread As Threading.Thread = New Threading.Thread(AddressOf ThreadedGameLoop)
            game_thread.Start()
        End If
    End Sub

    Private Sub GameLoop_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
        If _enabled Then
            Me.Enabled = Not _enabled
        End If
    End Sub

    Private Sub ThreadedGameLoop()
        Dim s As New Stopwatch
        Do
            s.Restart()

            While _enabled AndAlso s.ElapsedMilliseconds < _interval
            End While
            s.Stop()

            RaiseEvent Tick(Me, New GameLoopEventArgs() With {.ActualFPS = s.ElapsedMilliseconds, .TargetFPS = _interval})
        Loop Until Not _enabled
    End Sub

    Public Sub Start()
        If Not _enabled Then
            Me.Enabled = Not _enabled
        End If
    End Sub

    Public Sub [Stop]()
        If _enabled Then
            Me.Enabled = Not _enabled
        End If
    End Sub

    Sub New()
        _interval = 16.6
    End Sub

    Sub New(ByVal interval As Single)
        _interval = interval
    End Sub

End Class

Public Class GameLoopEventArgs
    Inherits EventArgs

    Private _actualFPS As Single
    Public Property ActualFPS As Single
        Get
            Return _actualFPS
        End Get
        Set(ByVal value As Single)
            If Not _actualFPS.Equals(value) Then
                _actualFPS = value
                Me.OnActualFPSChanged()
            End If
        End Set
    End Property

    Private _targetFPS As Single
    Public Property TargetFPS As Single
        Get
            Return _targetFPS
        End Get
        Set(ByVal value As Single)
            If Not _targetFPS.Equals(value) Then
                _targetFPS = value
                Me.OnTargetFPSChanged()
            End If
        End Set
    End Property

    Protected Overridable Sub OnActualFPSChanged()
        RaiseEvent ActualFPSChanged(Me, EventArgs.Empty)
    End Sub

    Protected Overridable Sub OnTargetFPSChanged()
        RaiseEvent TargetFPSChanged(Me, EventArgs.Empty)
    End Sub

    Public Event ActualFPSChanged(ByVal sender As Object, ByVal e As EventArgs)

    Public Event TargetFPSChanged(ByVal sender As Object, ByVal e As EventArgs)

End Class
```

In this example, I create a new component which is similar to a System.Timers.Timer class. Whenever the game loop is meant to be running a new thread is created and will not exit the thread until the game loop is set to stop. Inside of the loop I reset the stopwatch(which tracks how much time has elapsed) and do nothing until the time that has elapsed is equal to or greater than the desired interval. Once I reach the desired elapse time I stop the stopwatch and raise an event to signal that the user needs to update/draw/render the game and I also report the Actual vs. Target elapsed milliseconds.

The frame rate will vary depending on what device you are targeting. In this example, you will see that the frames-per-second is locked in at 60FPS(interval = 16.6) if the programmer simply creates a new GameLoop without passing any arguments. This is because the example targets computers that have a screen refresh rate of 60Hz. So how do you determine what the frame rate should be set at?

The simplest way is to check what the maximum refresh rate will be and set the frame rate equal to or less than that refresh rate. In the United States, the refresh rate for most monitors is 60Hz while in Europe the refresh rate for most monitors is 50Hz. If you have a television that has a refresh rate of 120Hz and you want to develop specifically for that TV, then lock your FPS at or below 120. You can get the exact refresh rate by using the ManagementObjectSearcher class.

My last bit of advice is that if you want to target multiple screens then the general rule of thumb is to have the FPS set between 30 and 60.

----------


## TizzyT

But isn't this essentially just a:



```
Public Event Tick()

Dim Interval as Long = CLng(stopwatch.Frequency / 60)
Dim NextTime as Long = 0

Public Sub Start()
   NextTime = Stopwatch.GetTimeStamp + Interval
   While true
      If Stopwatch.GetTimeStamp >= NextTime Then
         RaiseEvent Tick
         NextTime += Interval
      End If
   End While
End Sub
```

Just with some threading and event sugar?
This still uses essentially the same CPU as using the while loop by itself (which is a lot).

Edit: Here is my solution and performance difference on my PC.
http://pastebin.com/NnvJQCmK

@60FPS or (1000 / 60) Milliseconds
While Loop = ~14.5%
 GameLoop = ~14.5%
MicroTimer = ~00.7%

Specs:
CPU = Core i7 2600K
GPU = GTX 680
Ram = 8GB DDR3 1333

----------

