Board Logo
« The TIMER command »

Welcome Guest. Please Login or Register.
Oct 24th, 2014, 06:16am


Conforums Terms of Service | Membership Rules | Home | Search | Recent Posts | Notification | Format Your Message | Installation FAQ

This board is not meant for general discussion, it is meant for posting articles to help others.
For general discussions use the appropriate board, which best describes your problem area.

« Previous Topic | Next Topic »
Pages: 1  Notify Send Topic Print
 thread  Author  Topic: The TIMER command  (Read 3295 times)
uncleBen
Senior Member
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1679
xx The TIMER command
« Thread started on: Nov 13th, 2006, 11:59am »

TIMER command is very useful everywhere you need to create pauses in the program, control animations etc. As it is so powerful, it is also very hard to understand. This article attempts to explain how it works and demonstrates a few common ways to use it.

The syntax of the command is simple:
Quote:
TIMER num, [branch]
or
TIMER num, timerSub


num is numeric value saying how many milliseconds (1 second = 1000 milliseconds) must pass before program execution branches to the specified branch label or sub.

What often causes confusion is that the timer command doesn't do "anything" immediately. The program's execution will not stop and wait at the TIMER statement. Instead it tells the computer to send a special "signal" (timer event) to your program every num milliseconds.
This event is very similar to any other Windows events, such as buttons pressed, mouse clicked, menus selected etc, except it doesn't come from the user, but is generated by the computer. Just like user events, the timer events are only responded to and handled when the program is waiting at the WAIT statement.

1) TIMER command doesn't stop the program. The program runs up to the next WAIT command, and when the time is up, it will branch to the timer handler.

Example 1:
Code:
    timer 1000, [continue] '<- does NOT stop here
    print "Timer has been set"
    print "Starting to wait."
    WAIT '<- program stops here

[continue] '<- when time is up, execution branches here
    timer 0
    print "Time's up"
    end
 


2) TIMER event is recurring, not a one-time happening. The program will continue jumping to the timer event handler every num milliseconds until the timer is turned off with TIMER 0.

Example 2:
Code:
    timer 1000, [continue] '<- timer is set only once
    WAIT

[continue]
    n = n+1 '<- counts how many times program gets here
    if n < 11 then
        print "Timer event no "; n
        WAIT
        else
        print "Ok, that's all for now."
        timer 0 '<- turns the timer off
    end if
    end
 


3) You can use a variable to set the timer. When a new TIMER command is issued, the previous command is "cancelled" and the timer is reset with a new value and/or branch.
You can use this, for example, to make the pauses gradually shorter or longer.

Example 3:
Code:
    pause = 1500
    timer pause, [continue] '<- timer is set using a variable
    WAIT

[continue]
    print "Pause lasted "; pause; " milliseconds."
    pause = pause - 100 '<-make pause shorter
    if pause > 0 then
        timer pause, [continue] '<- resets the timer with a new value
        WAIT
        else
        print "Ok, that's all for now."
        timer 0 '<-turns the timer off
    end if
    end
 
« Last Edit: Nov 13th, 2006, 12:17pm by uncleBen » User IP Logged

Passing arrays to subroutines, functions that work with any types, quick string indexing and much more - JBExtensions.

Tired of Minesweeper? Try TomatoSweeper
uncleBen
Senior Member
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1679
xx Creating pauses and animations
« Reply #1 on: Nov 13th, 2006, 12:06pm »

So, how to create pauses in a program? One simple way would be to do it as in Example 1.
Another way is to use a user-defined sub like the following:
Code:
sub Pause milliseconds
    timer milliseconds, [continue]
    wait

[continue]
    timer 0
end sub
 


This has a great advantage: if you want to create several pauses in different places of the code, all you have to do is call this sub and pass it a value, how long you want the pause to be. Otherwise you would have to issue a new TIMER command for each pause, and also invent a unique branch label, where execution would resume.

The bad thing about this sub is, that it has a WAIT command inside it. That means that the program can take user input while inside the sub. Keep in mind, that inside a sub all branch labels outside it (except for [continue] which is inside the same sub) are invisible. So, if you used branches for event handlers, this would cause a branch not found error. To use this sub to create pauses, all the event handlers that the user might trigger during the pause need to be subs. That includes the TRAPCLOSE event.

Here's an example, how the sub is used to create a pause in a small program.
Code:
    nomainwin
    statictext #main.text, "Press the button", 10, 10, 100, 25
    button #main.button, "Press me!", buttonPressed, UL, 10, 40
    open "Timer example" for window as #main
    #main, "trapclose quit" '<- use sub to trap close!
    wait

sub buttonPressed handle$
    'Disable the button...
    #main.text, "Please wait a sec."
    #main.button, "!disable"
    #main.button, "Wait!"

    '...and wait for one second
    call Pause 1000

    'One second is up, enable the button again.
    #main.text, "Press the button"
    #main.button, "!enable"
    #main.button, "Press me!"
end sub

sub quit handle$
    timer 0
    close #main
    end
end sub

sub Pause milliseconds
    timer milliseconds, [continue]
    wait

[continue]
    timer 0
end sub
 


Another very common use for the TIMER command is to create animations, for example in a game. As an animation is created by drawing a new image after a short delay, you can take advantage of the recurring nature of the timer event.

A simple pattern is to set the timer once, and put all the code needed to update the display inside the timer branch which ends with a WAIT statement. Because the timer fires periodically, this code is run over and over again. So the general lay-out of code for animations would look something like that:

Quote:
    TIMER n, [animationLoop]
[animationLoop]
    'code needed to draw a frame of animation

    'if it is a game, check if the game is over
    'in which case simply turn the timer off with TIMER 0
    WAIT


Here's a small demo. Notice, that using an animation loop doesn't hinder you from getting user input. In this case, left-clicking changes the image. As this is not a real game, the animation runs forever or until you close the program - whichever comes first.
(Notice, run from the JB folder, as it uses sprites in JB sprites' folder.)

Code:
    nomainwin
    minLeft = 10
    maxRight = 350
    WindowWidth = 400
    WindowHeight = 300
    loadbmp "bg", "sprites\bg1.bmp"
    loadbmp "smiley1", "sprites\smiley.bmp"
    loadbmp "smiley2", "sprites\blockhead.bmp"
    open "Timer example: animation" for graphics_nsb as #main
    #main, "trapclose [quit]"

    #main, "down; background bg"
    #main, "addsprite smiley smiley1 smiley2"
    #main, "spritexy smiley 20 200"
    #main, "spritemovexy smiley 5 0"

    #main, "when leftButtonDown [changeImage]"
    timer 60, [animate] '<- set the timer

[animate] '"animation loop"
    #main, "drawsprites"
    #main, "spritexy? smiley x y"
    'check if the sprite needs to turn around
    if x <= minLeft then #main, "spritemovexy smiley 5 0"
    if x >= maxRight then #main, "spritemovexy smiley -5 0"
    wait

[changeImage]
    currentImage = currentImage xor 1 'toggle between 1 and 0 ...
    if currentImage = 1 then '... to change the sprite image
        #main, "spriteimage smiley smiley2"
        else
        #main, "spriteimage smiley smiley1"
    end if
    wait

[quit]
    timer 0
    unloadbmp "bg"
    unloadbmp "smiley1"
    unloadbmp "smiley2"
    close #main
    end
 
User IP Logged

Passing arrays to subroutines, functions that work with any types, quick string indexing and much more - JBExtensions.

Tired of Minesweeper? Try TomatoSweeper
uncleBen
Senior Member
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1679
xx Look out - danger!
« Reply #2 on: Nov 13th, 2006, 12:15pm »

There are some things you might need to be aware of when using TIMER.
Take a look at the following code snippet and guess how many times the message "Time is up" will be printed. Then run the code and see if you were right.

Code:
    timer 100, [timeUp]
    t = time$("ms")+350
    while time$("ms") < t : wend
    wait

[timeUp]
    timer 100, [finish] '<- reset the timer to go to [finish]
    print "Time is up"
    wait

[finish]
    timer 0
    end
 


Three times? Shouldn't there be only one message, because the timer is reset in the [timeUp] branch?

Here's what happens. The timer is set to fire every 100 milliseconds. But the while loop takes 350 ms to finish. During that time the timer fires 3 times. However, the program cannot branch to [timeUp] before it reaches the WAIT statement. Therefore the 3 instructions to go to [timeUp] are stored somewhere. When the program reaches the WAIT command, it will carry out all those instructions first. The new timer instruction to go to [finish] gets a 4th place in the queue.

So, timer events accumulate if the program cannot execute the branch. This is something to be avoided.
There are a few conditions where this might happen.

1) If you open a native JB dialog, while the timer is on (NOTICE, CONFIRM, FILEDIALOG). This will keep the program from reaching a WAIT statement, and it won't be able to respond to the timer events. As long as the user doesn't close the dialog, the timer events keep accumulating, and this might crash the program eventually.
Always turn the timer off before opening these dialog boxes.

Note: PROMPT may not work in a program that uses timers. If that is so, you'll need to create your own prompt box.

2) If the code in the animation loop takes longer to run that the timer interval. This would result in problems, because the user input cannot be processed immediately, but only after all the accumulated timer events have been dealt with. That means that it would take longer and longer before the program responds to user input.

It is quite hard to predict when this might happen. The program may run fine on your computer, but the users may experience problems if they have a slower computer.

If you suspect that this might happen, you'll need a more advanced techique to control your animation loop and stop the timer events from accumulating. The general strategy is to turn the timer off at the start of the animation loop, measure how long it took to run the animation code with time$("ms") and set the timer with an appropriate value at the end of the branch.

Also make sure you won't set the timer to a negative value: it is better to guarantee it is always set at least to a minimum value (for example 16 ms), so the program also pauses to check for possible user input.

It might look something like this:

Code:
    nomainwin
    statictext #main.text, "", 10, 10, 100, 24
    open "Advanced timer" for window as #main
    #main, "trapclose [quit]"

    pause = 500 'ideally the timer should fire every 0.5 seconds
    minimumPause = 16 'wait at least 16 ms to get user input

[animation]
    timer 0 '<- turn timer off
    startTime = time$("ms") '<- measure start time for the code block

    'some really busy code, that may take longer than 
    '500 ms to complete,
    'for example:
    q = int(rnd(1)*50000)
    #main.text, "Looping "; q; " times"
    for i = 1 to q : next i

    totalTime = time$("ms")-startTime '<- stop the stopwatch
    'take into account that it might be a new day!
    if totalTime < 0 then totalTime = totalTime + 86400000

    'Calculate how many milliseconds the TIMER should pause
    'so that the program would continue in 500 ms...
    milliSeconds = pause - totalTime

    '... or set the minimum pause it should wait, if the busy code
    'took more time to complete
    if milliSeconds < minimumPause then milliSeconds = minimumPause

    timer milliSeconds, [animation] 'set the timer
    wait

[quit]
    timer 0
    close #main
    end
 


This strategy ensures that the program responds normally to all user input, no matter how fast or slow the computer is. This may mean, of course, that on slower computers the animation might run more slowly.

Another danger with TIMER is, that if you make an error with it - for example, if the program can't find the timer branch - this tends to crash JustBasic completely. To avoid losing your code, save it before testing and/or make sure, that you have enabled creating back-ups.
« Last Edit: Nov 13th, 2006, 12:20pm by uncleBen » User IP Logged

Passing arrays to subroutines, functions that work with any types, quick string indexing and much more - JBExtensions.

Tired of Minesweeper? Try TomatoSweeper
silverbear47
Junior Member
ImageImageImage


member is offline

Avatar

Can't find a waving bear ...

YIM YIM
Homepage PM

Gender: Female
Posts: 122
xx Re: The TIMER command
« Reply #3 on: Nov 14th, 2006, 12:15am »

Wow - great description - I do believe you've cleared up a couple of things for me!!!! I hope others will benefit also!
I wish the description in HELP was as concise and straightforward. Thank you!!

daBear
User IP Logged

I tried it - I liked it - or not ...
Techno
Full Member
ImageImageImageImage


member is offline

Avatar




PM

Gender: Male
Posts: 312
xx Re: The TIMER command
« Reply #4 on: Nov 14th, 2006, 06:02am »

Same hear Silverbear. Maybe we should inform Alice of the wonderful Tut and she could update it.
User IP Logged

Techno

Simply put, JB ROCKS!!!
uncleBen
Senior Member
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1679
xx Re: The TIMER command
« Reply #5 on: Nov 14th, 2006, 07:47am »

on Nov 14th, 2006, 12:15am, silverbear47 wrote:
Wow - great description - I do believe you've cleared up a couple of things for me!!!! I hope others will benefit also!
I wish the description in HELP was as concise and straightforward. Thank you!!

daBear


Well, thank you.

The HELP pages are meant to be a quick reference, not a full-sized tutorial. It does actually contain most of the information in this tutorial.

You are always free to experiment with JB commands to find out more about them and see what works and what doesn't. It can't do too much harm smiley
User IP Logged

Passing arrays to subroutines, functions that work with any types, quick string indexing and much more - JBExtensions.

Tired of Minesweeper? Try TomatoSweeper
Techno
Full Member
ImageImageImageImage


member is offline

Avatar




PM

Gender: Male
Posts: 312
xx Re: The TIMER command
« Reply #6 on: Nov 14th, 2006, 2:34pm »

Except for some 'careful, high damage' commands, like KILL......... laugh laugh
User IP Logged

Techno

Simply put, JB ROCKS!!!
Pages: 1  Notify Send Topic Print
« Previous Topic | Next Topic »

Conforums Terms of Service | Membership Rules | Home | Search | Recent Posts | Notification | Format Your Message | Installation FAQ

Donate $6.99 for 50,000 Ad-Free Pageviews!

| |

This forum powered for FREE by Conforums ©
Sign up for your own Free Message Board today!
Terms of Service | Privacy Policy | Conforums Support | Parental Controls