-
Jul 16th, 2014, 09:30 AM
#1
[Vb.Net] Managed Game Loop
Description
Some people want the ease of use from a timer but want the accuracy from a game loop. Well this code here does just that. Use it like a timer, but expect high precision accuracy that automatically matches the computer monitor's refresh rate.
Prerequisites
Simply add the System.Management DLL to your references. If you computer does not support the QueryPerformance API's then an exception will be thrown.
Code
Code:
Option Strict On
Option Explicit On
<System.ComponentModel.DefaultEvent("Tick")> _
Public Class GameLoop
Inherits System.ComponentModel.Component
Private frequency As Long
Private waitThread As Threading.Thread
#Region "Api"
Private Declare Function QueryPerformanceCounter Lib "kernel32" (ByRef lpPerformanceCount As Long) As Integer
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (ByRef lpFrequency As Long) As Integer
#End Region
#Region "Events"
Public Event Tick(ByVal sender As Object, ByVal e As EventArgs)
#End Region
#Region "Methods"
Private Sub CheckCompatibility()
Dim test As Long
If Not CBool(QueryPerformanceCounter(test)) Then
Throw New Exception("High-resolution counter is not supported for this computer.")
End If
End Sub
Private Function GetRefreshRate() As Double
Dim query As System.Management.SelectQuery = New System.Management.SelectQuery("Win32_VideoController")
For Each mo As System.Management.ManagementObject In New System.Management.ManagementObjectSearcher(query).Get
Dim currentRate As Object = mo("CurrentRefreshRate")
If currentRate IsNot Nothing Then
Return CDbl(currentRate)
End If
Next
Return Nothing
End Function
Public Sub Start()
pEnabled = True
waitThread = New Threading.Thread(AddressOf Wait)
waitThread.IsBackground = True
waitThread.Start()
End Sub
Public Sub [Stop]()
Me.Enabled = False
End Sub
Private Sub Wait()
Dim counter1, counter2 As Long
QueryPerformanceCounter(counter1)
Do
QueryPerformanceCounter(counter2)
Loop Until (counter2 - counter1) / (frequency / 1000) >= 1000 / rate
RaiseEvent Tick(Me, EventArgs.Empty)
Console.WriteLine((counter2 - counter1) / (frequency / 1000))
If pEnabled Then
waitThread = New Threading.Thread(AddressOf Wait)
waitThread.IsBackground = True
waitThread.Start()
End If
End Sub
#End Region
#Region "New Constructor"
Sub New()
Call CheckCompatibility()
QueryPerformanceFrequency(frequency)
rate = GetRefreshRate()
End Sub
#End Region
#Region "Properties"
Private pEnabled As Boolean
<System.ComponentModel.Description("Gets or sets a value indicating whether the GameLoop should raise the Tick event.")> _
Public Property Enabled() As Boolean
Get
Return pEnabled
End Get
Set(ByVal value As Boolean)
If pEnabled <> value Then
pEnabled = value
If pEnabled Then RaiseEvent Tick(Me, EventArgs.Empty)
End If
End Set
End Property
Private rate As Double
<System.ComponentModel.Description("Gets or sets a value indicating the refresh rate of the computer's monitor.")> _
Public ReadOnly Property RefreshRate() As Double
Get
Return rate
End Get
End Property
#End Region
End Class
Acknowledgements
Jacob Roman - Showing me the QueryPerformance API's
TnTinMN - Helping with getting the refresh rate of the monitor
Last edited by dday9; Jul 17th, 2014 at 01:58 PM.
Reason: Added Acknowledgements
-
Jan 5th, 2015, 07:33 PM
#2
Re: [Vb.Net] Managed Game Loop
Hi dday,
This would be a really useful timer but I can't get it to work as described. To test it, I dragged a GameLoop component onto a form with a button, and coded the form as follows:
Code:
Private sw As Stopwatch
Dim prevMilliseconds As Integer
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
With GameLoop1
Console.WriteLine(.RefreshRate)
sw = Stopwatch.StartNew
.Start()
End With
End Sub
Private Sub GameLoop1_Tick(sender As Object, e As System.EventArgs) Handles GameLoop1.Tick
Dim s As Integer = CInt(sw.ElapsedMilliseconds)
Console.WriteLine(s - prevMilliseconds)
prevMilliseconds = s
End Sub
The output shows the Tick interval alternating between an exact-looking 16.66... milliseconds and a rather random interval of somewhere between 20 and 120 milliseconds, for example:
Code:
16,6667161647157
24
16,6666973679882
88
16,6669718002096
44
17,9118978585828
64
16,6671447301025
53
16,6669417254456
(excusez le decimal comma)
Of course, this could conceivably be due to my somewhat antiquated hardware (with WinXP no less) or maybe I have made a mistake somewhere. What result do you get when you run the same test?
BB
-
Jan 6th, 2015, 09:58 AM
#3
Re: [Vb.Net] Managed Game Loop
What I have found is that the QueryPerformance API's are very hardware intensive, so if you do not have up-to-date hardware then it can run sporadically.
Another thing that I noticed is that if you're running a dual monitor, then you'll get optimal results by running the program on the primary screen.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|