Results 1 to 9 of 9

Thread: [RESOLVED] Need help catching the Tick from a fast timer

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Resolved [RESOLVED] Need help catching the Tick from a fast timer

    I found what looks like a nice fast Timer component and I need help catching the Tick event. My program has a form with Start, Stop, and Quit Buttons. I think I am close to making it work, but can't figure out how to write the sub that catches the tick.

    The problem area is the commented out sub at the bottom of the form code.
    In case it helps, here is the link to the timer I found:
    http://www.vbforums.com/showthread.php?770513-High-Precision-Timer

    Here is the form design:
    Name:  form1.jpg
Views: 499
Size:  6.9 KB

    Here is the Form code:
    Code:
    Class Form1
        Public T1 As New FastTimer
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            T1.Start()
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Application.Exit()
        End Sub
    
        Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
            T1.Stop()
        End Sub
    
        'Private Sub T1_Tick(sender As Object, e As EventArgs) Handles T1.Tick
        'End Sub
    
    End Class
    Here is the PrecisionTimer code:
    Code:
    Option Strict On
    Option Explicit On
    <System.ComponentModel.DefaultEvent("Tick")> _
    Public Class PrecisionTimer
        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
    
        Public Sub Start()
            Me.Enabled = 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)
    
            If Me.LowerCpuUsage Then
    
                Do
                    QueryPerformanceCounter(counter2)
                    Threading.Thread.Sleep(2)
                Loop Until (counter2 - counter1) / (frequency / 1000) >= Me.Interval
    
            Else
    
                Do
                    QueryPerformanceCounter(counter2)
                Loop Until (counter2 - counter1) / (frequency / 1000) >= Me.Interval
    
            End If
    
            Console.WriteLine((counter2 - counter1) / (frequency / 1000))
    
            RaiseEvent Tick(Me, EventArgs.Empty)
    
            If Me.AutoReset Then
                Me.Enabled = False
            ElseIf Me.Enabled Then
                waitThread = New Threading.Thread(AddressOf Wait)
                waitThread.Start()
            End If
    
        End Sub
    
    #End Region
    
    #Region "New Constructor"
    
        Sub New()
            Call CheckCompatibility()
            QueryPerformanceFrequency(frequency)
            Me.Interval = 100
        End Sub
    
        Sub New(ByVal interval As Double)
            Call CheckCompatibility()
            QueryPerformanceFrequency(frequency)
            Me.Interval = interval
        End Sub
    
    #End Region
    
    #Region "Properties"
    
        Public Property AutoReset As Boolean
        Private pEnabled As Boolean
        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
        Public Property Interval As Double
        Public Property LowerCpuUsage As Boolean
    
    #End Region
    
    End Class

  2. #2
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,044

    Re: Need help catching the Tick from a fast timer

    So what's the problem? There is a Tick event, you have a handler (commented out), so what's the issue?

    The tick is being raised on a different thread, so it won't be on the UI thread. That could be an issue for you. Could be other things, too. Best if you tell us.
    My usual boring signature: Nothing

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Re: Need help catching the Tick from a fast timer

    Sorry I was not clear. If you un-comment the handler you will see it generates an error. The problem is that I don't know how to write a working handler. I guess I wanted to show that I was at least trying to figure it out myself. If the problem is like you say on a different thread, then how is that resolved so the main form can be notified of the event?

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Need help catching the Tick from a fast timer

    Quote Originally Posted by PickyBiker View Post
    If you un-comment the handler you will see it generates an error.
    Maybe, rather than us having to create a project and test that for ourselves, you could just tell us what the error message is and exactly where it occurs. My guess would be that there's a cross-threading issue, which you've already been warned about. We shouldn't have to guess though.

  5. #5

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Re: Need help catching the Tick from a fast timer

    The editor highlights the handler at "handles T1.Tick" With the T1 highlighted.

    The message when I hover over it reads:
    Handles clause requires a WithEvents variable defined in the containing type or one of its base types.
    When I try to compile it, the message reads:
    Error BC30506 Handles clause requires a WithEvents variable defined in the containing type or one of its base types.

  6. #6
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Need help catching the Tick from a fast timer

    The issue is obvious when we're provided with all the relevant information. You will save us all some time if you do that in post #1 in future. As the error message says, if you want to use a variable in a Handles clause then you need to declare that variable WithEvents. If you add a control or component to your form in the designer then that happens automatically. Given that that FastTimer class inherits Component, you can add it to a form from the Toolbox. If you want to declare the variable in code though, you need to use the WithEvents keyword yourself, i.e.
    vb.net Code:
    1. Public WithEvents T1 As New FastTimer
    By the way, why is that field declared Public? Are you planning to access it outside the form?

  7. #7

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Re: Need help catching the Tick from a fast timer

    Okay, I was trying to make things easier by providing all the code. Obviously that was a bad idea... Message received. The field you asked about is public because I wasn't thinking about the scope of things, I was focused on seeing if I could get the fast timers working. I'll be the first to admit I am not a good programmer. I do this stuff as a hobby in retirement. I do however appreciate the effort others put in to help out on occasion.

    The code you provided fixes the error. Thank you.

    The goal here is to use the fast timer to run a game loop on a fairly accurate period of about 10ms. The regular VB Timer is not fast enough and is quite inaccurate.

    There is still one issue with cross thread access. I will try to work through that on my own for a while with some research.

    Thanks again for the help.

  8. #8
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,044

    Re: Need help catching the Tick from a fast timer

    The code WAS useful, it was just not enough. For one thing, you are quite likely to run into a second problem pretty quickly because the tick is raised on a different thread from the UI thread. What was missing was the text of the error message. With just the code or just the message, there isn't really enough to know what the problem was. You need both.

    I was also going to ask what you were using the timer for, because there aren't that many uses that will be all that good. You've found one of the bad ones. Consider that your monitor updates at (most likely) about 60 Hz, yet you are talking about a game loop that will run at 100 Hz. Therefore, you'd be needing to update the screen not every time through the loop, but roughly once every other time through the game loop.

    I haven't dealt with game loops in a long time, but you might consider using the Stopwatch object rather than a timer. This is kind of an inverted way to think about it, but it works pretty well. Your loop might have these steps:

    1) Decide what to do.
    2) Move characters around.
    3) Draw Screen

    The first or second step might take a LONG time, or virtually no time at all, depending on how many characters there are, whether or not they decide to move, and even whether or not they need to make a decision. For example, a dead character wouldn't have to think about where to go, so it would be able to skip steps 1 and 2.

    So, you don't really know how long the computer will take for the three steps. If you use a timer for that loop, then you may either get through the work LONG before the timer ticks, while other times you don't get through it all before the timer ticks. Better would be to perform step 3 whenever step 1 and 2 have both been finished, but if you do that, sometimes it will be fast and other times it will be slow. That's where the Stopwatch object comes in. The loop become more like this:

    1) Start timing
    2) Decide what to do
    3) Move characters around
    4) Draw Screen
    5) Stop timing

    Whenever you get to step 3, you pass in the elapsed time from the last loop. The characters have a velocity (distance/time), so you pass in the number of milliseconds since the last iteration. Sometimes the time for the last loop was really short, in which case the characters move very little because they have very little time to move. Other times, the length of the last loop was slow, in which case the characters move further, because they'll have had time to move further.

    This will efficiently use the computers time. If decisions are quick, then the frame rate will be fast. If the decisions are slow, then the movement will appear jerky because it won't be drawing as fast. You've likely already seen games like that. Sometimes they are smooth, other times they are laggy. That's because they are using a loop like this. The screen draws only in that step 4. If the earlier steps took a long time, the screen seems to jump around. If the earlier steps were quick, then the behavior is smooth.
    My usual boring signature: Nothing

  9. #9
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Need help catching the Tick from a fast timer

    Quote Originally Posted by PickyBiker View Post
    Okay, I was trying to make things easier by providing all the code. Obviously that was a bad idea... Message received.
    I wasn't suggesting that you shouldn't provide relevant code. That is definitely a good idea. In fact, we may well have been able to spot that issue just from the code. The thing is, because you didn't provide the error message, we had no idea what we were looking for and overlooked that issue with the code. As soon as the error message was provided, I knew exactly what to look for and could immediately see what was wrong with the code. That's exactly why specific error messages are provided. They are a diagnostic tool so logic dictates that you pass them on to those whom you want to help diagnose your issue.

    Also, even if we had spotted that issue, we wouldn't necessarily know whether it was the one that you were posting about in the first place. It may have just been one issue that you weren't even aware of. That's happened before more than once.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width