Simplest way to make a timer

BlitzMax Forums/BlitzMax Beginners Area/Simplest way to make a timer

ragtag(Posted 2006) [#1]
I'm doing a simple Blackjack game and need a timer when dealing the cards, so there is about 0.3 sec between each card. I can't just pause the whole main loop as there is animation going on (i.e. UpdateAll() needs to run each frame). What's the best way to go about this?

The program is running a while/wend loop with a select/case statement which runs a Method at the time. I've got something that works, using a global timer variable, but it feels a bit clunky so I was wondering if there is a better way to do it?


FlameDuck(Posted 2006) [#2]
CreateTimer(). About 0.3 seconds is about 300 milliseconds.


ragtag(Posted 2006) [#3]
Oops...missed that one. :/

Now I feel like the guy asking the stupid questions. :) I was puttin the current millisecs into a global and ckecking that. I knew there was a more straight forward way.


ragtag(Posted 2006) [#4]
Hmmm....looked at it a bit closer, and it seems create timer is not what I want. From what I can figure out it seems WaitTimer() will actually stop the progress of the program. What I want to do is keep the main loop updating, but only do something every n-th millisecond.

Here is what I wound up doing, which I'm quite happy with. (migh want to change the name of the Type as I think it's the same name as CreateTimer uses.

' Timer function. Use [ If Time.Tick() Then something... ]
Type TTimer
	Field Start:Int		' Start time.
	Field Ticks:Int		' The number of times the timer has clicked.
	Field Wait:Int		' Number of millisecs to wait between ticks.
	
	' Returns true if the timer ticks. Add's one to Tick count.
	Method Tick:Int()
		If MilliSecs() > Start + Wait
			Ticks:+1
			Start = MilliSecs()
			Return 1
		Else
			Return 0
		End If
	End Method

	' Creates a timer. Takes time to wait between ticks as input.
	Function Create:TTimer( inWait:Int = 60 )
		Local Time:TTimer = New TTimer
		Time.Start = MilliSecs()
		Time.Ticks = 0
		Time.Wait = inWait
		Return Time
	End Function
End Type


All I then need to do in my code is create the timer like so....

Global Timer:TTimer = TTimer.Create( 300 )


And then if I want to check if it's running I can do...

If Timer.Tick() Then ' Do something


Works very nicely in the Blackjack game I'm working on. :)


FlameDuck(Posted 2006) [#5]
From what I can figure out it seems WaitTimer() will actually stop the progress of the program.
Use PollEvent() instead.


Dreamora(Posted 2006) [#6]
Or create a new event and an event handling function. Then you don't have to think about it anymore and it will execute itself without being part of your running code.

For the how, I can really advice checking this thread:
Thread on event handling with a timer


ImaginaryHuman(Posted 2006) [#7]
I've also been looking into doing something basically the same, whereby I execute a number of uninterrupted functions at the start of each frame, then there is some random amount of time left between those functions being completed and the end of the frame time arriving (as I need to do a Flip `on time`). What I wanted to do is, in the random amount of time at the end of each frame, keep executing a given function as many times `as possible` before the time runs out.

The only way I can see to do this, is to repeatedly call Millisecs() in a loop, calling the function once or a few times (if it's small), and then when Millisecs() gets to a certain point based on the start time of the frame I break out of it, do the Flip, then start with a new frame.

So basically I need a sort of multitasking system, where a certain piece of code (a function) is given x amount of time and is allowed to keep running for that long, then has to stop to allow other predetermined things to process on-time. But managing a `timeslice`, as in your case as well, ragtag, seems to be only possibly by constantly checking Millisecs(). I'm finding that this can produce quite a significant overhead. In fact, it can eat up more than 10 times the processing time compared with just excecuting the given function a predetermined number of times without timing it.

You may want to cut down your calls to Millisecs() as much as possible. Like, you don't need to say "If Millisecs()", and then when that If is satisfied go on to say "Start=Millisecs()". You may be better of saying "Milli=Millisecs()" outside the If statement, and then "If Milli>Start+Wait Then Start=Milli". That should speed it up quite a bit because, well, I don't know how efficiently Max looks at what the millisecs is, but I am guessing each time you call it it is doing some kind of memory/access/calculation to `go look` for the time, rather than have the o/s conveniently place changes in a buffer only when they occur, with the buffer then being polled more efficiently. In my case I cut out as many calls to Millisecs() as I could possibly do and this helped with how many function calls I could make. I guess it depends how big the chunks of code are that you call in the time you have.

I could not see a better or more efficient way to do this yet. The idea of using events and hooks sounds great, in the sense that you can then override whatever is happening the instant that the timer event occurs, so you don't have to keep looking at the timer to see its value or to test it. But then I'm not sure how that timer is being managed by the o/s. Does the o/s spend as much time polling the timer and then create the event, or is the timer in hardware that triggers some code only as an interrupt when it `counts down` to zero? That would be ideal. But then again, and correct me if I'm wrong, don't you have to have MaxGUI to use hooks and events like that?

For now I am continuing to just call Millisecs() lots of times inbetween executing small chunks of code, until available time is used up. It's not efficient and its a lot slower than regular untimed code, but it does work.

I thought about timing how long the excecuting piece of code takes to excecute, then calculate how many times it can approximately be called within a given time frame to use up the time, then just do a For/Next loop calling it x number of times - saving any need to check Millisecs(). But then, you have to test it at runtime and if you've got a lot of possible functions to execute that could be inconvenient. You can't really estimate execution time because there is so much different hardware. So I dunno. Keep working at it.


ImaginaryHuman(Posted 2006) [#8]
I did a test and found that PollEvent() is actually 40 times slower than Millisecs().

So the idea of doing lots of poll events is not at all appealing.

It'd be great to have a hardware timer with a hardware interrupt function, but alas. Gone are the days of being intimate with the hardware.

Can somebody who has the capability do a test to see if setting up an event hook and having it execute only when a given time is met, is faster than using a Millisecs() timer loop?


ragtag(Posted 2006) [#9]
Thanks for the feedback. I'm almost finished with my Blackjack game, will post it once done. Only got saving and some artwork left. :)

I looked into Hooks, Events and CreateTimer. Took a bit to get my head around, but I think I kind of got it. May be usefull for future projects..

I'm just using calls to MilliSecs(). The game is so simple that I don't really need to worry about slowdown. It runs smooth on my 1.3ghz iBook, so should run fine on most everything.

I guess if you're running with a fixed frame rate (not delta time), you could just use a global frame count instead of MilliSecs, and just increase it by 1 for every game loop.