Jump to content

Photo

bB and timers help needed


19 replies to this topic

#1 jubileebop OFFLINE  

jubileebop

    Combat Commando

  • 7 posts

Posted Sat Sep 18, 2010 5:45 AM

Hi,
I need help with a racing game:
1. I'd like to have a timer from start of race running on screen. I looked for a bB implementation of timers and found nothing. Does anyone know how to?
2. I'd also like to time the interval between key presses (fire button).

Thanks

#2 yuppicide OFFLINE  

yuppicide

    I am the Black Knight. Give me your money!

  • 6,933 posts
  • Location:New Jersey

Posted Sat Sep 18, 2010 8:34 AM

Does your game use the score? You could use that as a counter and count down.. decrease the score every so many frames.

For the interval between key presses you could set a flag when you press the button..

like

set the flag like this:


 if a = 0 then goto fire else nofire

fire:
 if joy0fire then a = 1

nofire:

Something like that..

#3 jubileebop OFFLINE  

jubileebop

    Combat Commando

  • Topic Starter
  • 7 posts

Posted Sat Sep 18, 2010 8:51 AM

Does your game use the score? You could use that as a counter and count down.. decrease the score every so many frames.


Yes, I will use the score but I will format it to look like a timer. If I understand correctly.. I count 60 frames and that is one second on my timer, right? How do I count frames? (forgive my newbie question)

#4 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 25,233 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sat Sep 18, 2010 9:24 AM

Yes, I will use the score but I will format it to look like a timer. If I understand correctly.. I count 60 frames and that is one second on my timer, right? How do I count frames? (forgive my newbie question)

Are you talking about a simple counter like this:

dim Time_Counter = a

   COLUBK = $C4
   scorecolor = $1C


Main_Loop


   Time_Counter = Time_Counter + 1

   if Time_Counter < 60 then Skip_Timer

   Time_Counter = 0

   score = score + 1

Skip_Timer


   drawscreen


   goto Main_Loop

Or something more advanced?

Edited by Random Terrain, Sat Sep 18, 2010 9:25 AM.


#5 jubileebop OFFLINE  

jubileebop

    Combat Commando

  • Topic Starter
  • 7 posts

Posted Sat Sep 18, 2010 10:33 AM

I replaced with Time_Counter with Frame_Counter. This is what I had in mind:

   dim Frame_Counter = f

   COLUBK = $C4
   scorecolor = $1C

Main_Loop

   Frame_Counter = Frame_Counter + 1
   
   if Frame_Counter < 60 then Skip_Timer

   score = score + 1
   Frame_Counter = 0
  
Skip_Timer

   drawscreen

   goto Main_Loop

1. Is the concept OK? Is there a better way to tell time?
2. How do I know when frame advanced (when does Frame_Counter = Frame_Counter + 1)?

Thanks

#6 RevEng ONLINE  

RevEng

    River Patroller

  • 3,477 posts
  • bit player
  • Location:Canada

Posted Sat Sep 18, 2010 11:24 AM

In most cases you can consider the time between "drawscreen" commands to be 1/60th of a second in NTSC.

Its actually slightly less than 1/60 and I'm assuming you didn't overun the allotted cycles in bB, but assuming that each tick of your timer is 1/60th of a second will work unless you're implementing a "clock program" that needs to accurately display the time for months.

You should be aware that your frame counter will reach 255 and start over at zero again. If you need more time you'll need another variable for minutes. (or 256*seconds)

Yes, this is the best method. The 2600 has a hardware timer, but its only useful for shorter intervals, and its already used by bB.

#7 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,455 posts
  • Location:Georgia, USA

Posted Sat Sep 18, 2010 12:01 PM

If your program has a loop that contains one and only one 'drawscreen' statement in it, the loop will execute at a frequency of either 60 times a second or 50 times a second, depending on whether your program contains a 'set tv ntsc' or 'set tv pal' statement. Note that 'set tv ntsc' is the default, so if you omit the 'set tv' statement, the frame rate will be 60 times a second.

Actually, 60 times a second and 50 times a second are the approximate frame rates, so if you use a timer that's driven by the frame rate, it won't keep accurate time, but it should be more than accurate enough for most games.

Here's a simple program that displays hours, minutes, and seconds in the score. You can reset the timer by pressing the joystick button. If you have an accurate clock that displays seconds, and reset the timer to coincide with 0 seconds on the clock, then let the program run unattended for a while, you should be able to see that the seconds do not stay in sync between the clock and the program.

   dim frames=a
   dim seconds=b
   dim minutes=c
   dim hours=d

   scorecolor=$0E

loop

   if joy0fire then gosub reset_timer

   drawscreen

   frames=frames+1
   if frames=60 then frames=0:seconds=seconds+1:score=score+1
   if seconds=60 then seconds=0:minutes=minutes+1:score=score+40
   if minutes=60 then minutes=0:hours=hours+1:score=score+4000

   goto loop

reset_timer

   frames=0:seconds=0:minutes=0:hours=0:score=0

   return
To make it PAL/50 compatible, add 'set tv pal' to it, and change the line after 'frames=frames+1' to 'if frames=50' instead of 'if frames=60'.

For a racing game, you probably want to display the fractions of a second, so you could take out the hours and change the lines between 'drawscreen' and 'goto loop' to be as follows:

   frames=frames+1:score=score+1
   if frames=60 then frames=0:seconds=seconds+1:score=score+40
   if seconds=60 then seconds=0:minutes=minutes+1:score=score+4000
Again, you would change it to 'if frames=50' for a PAL/50 game.

If you 'dim' the three bytes of the score, you could use them directly instead of having to use three variables for 'frames', 'seconds', and 'minutes', as follows:

   dim minutes=score
   dim seconds=score+1
   dim frames=score+2

   scorecolor=$0E

loop

   if joy0fire then gosub reset_timer

   drawscreen

   score=score+1
   if frames=$60 then score=score+40
   if seconds=$60 then score=score+4000

   goto loop

reset_timer

   score=0

   return
I'm not certain what you mean by your other question, so I'll answer it two ways.

If you want to know the time between two presses of the fire button but want the timer to keep running, you'll need to have separate variables to keep track of the timing between presses of the fire button.

But if you want the fire button to stop the timer, you can just use the score, as follows:

   dim minutes=score
   dim seconds=score+1
   dim frames=score+2
   dim timer_running=a

   scorecolor=$0E

loop

   if joy0fire then timer_running=timer_running^1

   drawscreen

   if timer_running then score=score+1
   if frames=$60 then score=score+40
   if seconds=$60 then score=score+4000

   goto loop
This isn't perfect, because you really need to debounce the fire button, otherwise it's almost impossible to press it just once.

Michael

Edited by SeaGtGruff, Sat Sep 18, 2010 12:04 PM.


#8 Nukey Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 21,124 posts
  • Location:The land of Gorch

Posted Sat Sep 18, 2010 12:44 PM

As long as the total race time would not exceed 100 minutes (which is plenty), you could divide the existing score to include framecounting as the low 2 digits (so they would be the means to display 50th or 60th fractions of a second). Saves a variable...and the total race time can go up to 99:59.59 or 99:59.49 before rolling over. Otherwise, you could use floating-point multiplication to approximate 100th's of a second.

#9 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,455 posts
  • Location:Georgia, USA

Posted Sat Sep 18, 2010 1:04 PM

As long as the total race time would not exceed 100 minutes (which is plenty), you could divide the existing score to include framecounting as the low 2 digits (so they would be the means to display 50th or 60th fractions of a second). Saves a variable...and the total race time can go up to 99:59.59 or 99:59.49 before rolling over. Otherwise, you could use floating-point multiplication to approximate 100th's of a second.

Crikey, I didn't think about how PAL/50 would display 00 to 49 instead of 00 to 59 in the 'jiffies' digits (which I called 'frames'). But I don't know about floating-point multiplication-- not that it wouldn't work, because obviously it will. I'm just thinking it might be simpler/faster to use fixed-point addition, or maybe use a table lookup (which would take either 50, 60, or 110 extra bytes of ROM for the table[s]). Or if you let the frames go up to 59 for NTSC/60 or PAL/60, you'd need to change them only for NTSC/50 or PAL/50.

Michael

#10 Nukey Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 21,124 posts
  • Location:The land of Gorch

Posted Sat Sep 18, 2010 2:58 PM

Might be better to just use 10ths of a second instead (updating every 5 or 6 frames, depending on refresh rate)...then use the leftover sprite position from the 6-digit display to use as a decimal point. In that case, 1 byte of temp ram would be needed to hold the actual vs. displayed value of score3.

#11 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,455 posts
  • Location:Georgia, USA

Posted Sat Sep 18, 2010 9:48 PM

I figured out how to do hundredths of a second without any ROM tables, fixed-point or floating-point math, or extra variables.

For PAL, just add 2 to the score each frame. When you get to 50 frames, the seconds will automatically increment by themselves! Then all you need to do is check to see when you've reached 60 seconds so you can increment the minutes.

For NTSC, you need to add 5 for every 3 frames, or 1-and-2/3 every frame. With a digital display you'd drop any remainders instead of rounding up, so the addition will follow the pattern of +1, +2, +2, +1, +2, +2, +1, +2, +2, etc. Again, the seconds will automatically increment themselves when you get to 60 frames.

If you don't want to waste a variable for keeping track of whether you're using NTSC or PAL, just set a const named ntsc. Set it to 1 for NTSC, or to 0 for PAL.

   set tv ntsc
   const ntsc=1

   dim minutes=score
   dim seconds=score+1
   dim frames=score+2

   scorecolor=$0E

loop

   if joy0fire then score=0

   drawscreen

   score=score+2
   if !ntsc then skip_ntsc

   temp1=frames&$0F
   if temp1=2 || temp1=7 then score=score-1

skip_ntsc

   if seconds=$60 then score=score+4000

   goto loop
For PAL, just change the first two lines:

   set tv pal
   const ntsc=0
It goes ahead and adds 2 to score, since that will be the most likely addition. Then, if it's PAL (or not NTSC), it skips ahead to check the seconds. Otherwise (if it *is* NTSC) it checks the last digit of the already-incremented score. If the last digit is 2 or 7, it subtracts 1 from the score. So for NTSC the pattern for the hundredths of a second will be as follows:

00 (or .0000)
01 (or .0167)
03 (or .0333)
05 (or .0500)
06 (or .0667)
08 (or .0833)
10 (or .1000)
11 (etc.)
13
15
16
18
20
21
23
25
26
28
30
etc.

Of course, this is just using the score "as is"-- no extra characters for fancy formatting. To put a colon in the 2nd position would require changing the if-then for the seconds check, and would limit you to a maximum of 9 minutes and 59+ seconds. To put a decimal in the 5th position would require using a variable for the frame counter, as Nukey Shay said, but the logic would be a bit simpler than the example above, since you'd just increment the 6th digit every 5 or 6 frames depending on the TV type. It would still be a little tricky, because you'd have to make sure the addition doesn't destroy the decimal point, and manually update the seconds as needed.

Michael

#12 Nukey Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 21,124 posts
  • Location:The land of Gorch

Posted Sat Sep 18, 2010 10:14 PM

Alternate .asm methods for colon/decimals:

Different digit GFX (those with and without such characters)...no extra cycles needed in the display loop. When the kernel is setting the LSB's or MSB's for a digit, just bump it to the alternate set for the digits you want affected.

Using the ball sprite as divider(s)...7 cycles needed in the display loop, none if the divider is just a vertical line as tall as the digits.

When printing the score, is this a seperate bB module? I noticed that a custom logo display has been added which can be displayed above scores. Since that is the case, you could potentially use that routine and leave the score alone for other uses.

#13 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,455 posts
  • Location:Georgia, USA

Posted Sun Sep 19, 2010 1:43 AM

Here's an example that displays an edited timer-- minutes in the 1st position, a colon in the 2nd position, seconds in the 3rd and 4th positions, a decimal point in the 5th position, and tenths of a second in the 6th position. That means the timer can go up to only 9:59.9 before wrapping around to 0:00.0, which should probably be okay for a racing game. The program will work for either NTSC/60 or PAL/50. You'll need a modified includes file and score graphics file (attached below).

   includesfile timer.inc

   set tv ntsc
   const one_tenth=6

   dim minutes=score
   dim seconds=score+1
   dim tenths=score+2
   dim frames=a

   scorecolor=$0E

loop

   if joy0fire then score=0:frames=0

   tenths=tenths|$A0
   minutes=minutes|$0B

   drawscreen

   frames=frames+1:if frames<>one_tenth then skip_tenths

   frames=0
   tenths=tenths&$0F
   minutes=minutes&$F0
   score=score+1
   if tenths=$10 then score=score+90
   if seconds=$60 then score=score+94000

skip_tenths

   goto loop
Since the score is a BCD number, and we want to use all the decimal digits (0 through 9), I defined "digit 10" ($A) to be a decimal point and "digit 11" ($B) to be a colon. If you want to set any of the score's digits to a non-BCD value (like $A or $B), you must directly set the appropriate byte of the score (e.g., tenths=$A0 or seconds=$0B). However, if you try to perform any math on the score while any of its digits contain a non-BCD value, the result will probably be garbled, or the new value probably won't be what you expected. So this example clears the colon and decimal from the score before doing any math on the score. Then the colon and decimal are restored to the score just before calling drawscreen.

To change it so it will work for PAL/50, change "set tv ntsc" to "set tv pal", and change "const one_tenth=6" to "const one_tenth=5".

Rather than alter the original score_graphics.asm file, I copied it to score_graphics_timer.asm and added the two extra characters. That means you need to use a modified includes file instead of the default.inc file, so batari Basic will compile the program with the score_graphics_timer.asm file. I copied default.inc to timer.inc and changed the score graphics filename. That means you must put an "includesfile timer.inc" statement at the beginning of the program so batari Basic will know it's supposed to compile the program using the timer.inc includes file.

If I remember correctly, the forum doesn't like you to attach .asm files, so I've zipped the includes file, score graphics file, and program file together. An NTSC/60 .bin and a PAL/50 .bin are also provided so you can run the program without having to compile it first.

Michael

Attached Thumbnails

  • timer.png

Attached Files



#14 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,455 posts
  • Location:Georgia, USA

Posted Sun Sep 19, 2010 1:50 AM

When printing the score, is this a seperate bB module? I noticed that a custom logo display has been added which can be displayed above scores. Since that is the case, you could potentially use that routine and leave the score alone for other uses.

The score is part of the kernel, but there's an assembler directive between the game area and the score area that will call a minikernel routine if one's included, so it would be possible to put the timer display in a minikernel and leave the score alone.

Michael

#15 jubileebop OFFLINE  

jubileebop

    Combat Commando

  • Topic Starter
  • 7 posts

Posted Sun Sep 19, 2010 6:08 PM

This was very helpful THNX!
Special thanks to River Patroller...

#16 theloon OFFLINE  

theloon

    Quadrunner

  • 8,059 posts

Posted Sun Sep 19, 2010 9:27 PM

SeaGtGruff and the others already gave the proper answer but..

Another technique is to update a single counter variable ("counter = counter + 1") every iteration of the main loop and just use single bits out of it to activate events.

I find that counter{0} is great for flip-flopping things every frame and counter{4} is good for walking animations.

#17 Nukey Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 21,124 posts
  • Location:The land of Gorch

Posted Sun Sep 19, 2010 10:11 PM

The score is part of the kernel, but there's an assembler directive between the game area and the score area that will call a minikernel routine if one's included, so it would be possible to put the timer display in a minikernel and leave the score alone.


What I mean by "seperate" is can that routine built into the main kernel be called more than once in the display? No need to reinvent the wheel...if it can be avoided. It would just be a matter of swapping ram used for the score with ram that is used for the timer, JSR to the display loop, then swap them back.

#18 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,455 posts
  • Location:Georgia, USA

Posted Sun Sep 19, 2010 11:39 PM

The score is part of the kernel, but there's an assembler directive between the game area and the score area that will call a minikernel routine if one's included, so it would be possible to put the timer display in a minikernel and leave the score alone.


What I mean by "seperate" is can that routine built into the main kernel be called more than once in the display? No need to reinvent the wheel...if it can be avoided. It would just be a matter of swapping ram used for the score with ram that is used for the timer, JSR to the display loop, then swap them back.

No, it isn't separate in that sense. But the kernel could be customized to make the score routine a separate subroutine so it could be called (reused) for other situations.

On the other hand, if you're talking about swapping between the score and something else on different frames, I did it a while back to display an inventory strip without destroying the score. I customized the kernel to recognize two optional keywords named "userscore_flag" and "userscore_page," and I think batari might be planning to include those customizations in the next release of the standard kernel (they were easy to do, and don't mess up the timing). So that could be used to swap back and forth between the normal score and a timer that uses custom characters.

Michael

#19 jubileebop OFFLINE  

jubileebop

    Combat Commando

  • Topic Starter
  • 7 posts

Posted Sat Sep 25, 2010 7:06 AM

Hi,
I embedded your code for the timer in my game (and copied timer.inc and score graphics .asm to my game folder). My problem is that the dots (one and two) are displayed as boxes!

Another problem I'm having is that the counter is blinking becuase of pfscroll down. I thought that counter was seperate from playfield (shouldn't be effected by vertical scrolling).

Thanks

Attached Thumbnails

  • score_timer.JPG


#20 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,455 posts
  • Location:Georgia, USA

Posted Sat Sep 25, 2010 12:44 PM

I embedded your code for the timer in my game (and copied timer.inc and score graphics .asm to my game folder). My problem is that the dots (one and two) are displayed as boxes!

Make sure you put "includesfile timer.inc" at the very beginning of your program-- it must be the first line, before anything else.

You also need to set the nibbles (or BCD digits) to display the colon and the decimal point right before calling "drawscreen." Then you have to clear them before doing any math on the score:

   tenths=tenths|$A0 : rem * set the decimal point
   minutes=minutes|$0B : rem * set the colon

   drawscreen

   tenths=tenths&$0F : rem * clear the decimal point
   minutes=minutes&$F0 : rem * clear the colon
In the code snippet above, I removed some lines between "drawscreen" and where you clear the decimal point and colon, to help emphasize the placement of the statements relative to "drawscreen"-- but you need those other lines in there, so don't take them out.

Another problem I'm having is that the counter is blinking becuase of pfscroll down. I thought that counter was seperate from playfield (shouldn't be effected by vertical scrolling).

It is separate, and it shouldn't be affected by scrolling the playfield. I just added a playfield to my example and scrolled it, and it didn't affect the timer-- no flickering or blinking. The only thing I can think of is that your loop may be too long, causing the screen to roll. If you're using the Stella emulator, press Alt-L while the program is running, to display the number of lines in the upper left corner-- they should remain steady at either 262 (for NTSC) or 312 (for PAL). If they don't, you'll need to figure out what you can do to save time. Note that you don't need to put playfield and player statements inside your loop, because once you set them, they'll keep their graphics until you need to set them again (i.e., if their graphics need to change). So if you've added a playfield statement inside your loop, move it to just above where the loop begins.

Also note that there's an exception to what I just said about the graphics. You *do* need to put any COLUP0 and COLUP1 statements inside your loop, so the player colors will get set properly each time you draw the screen-- otherwise the player colors will get changed to the scorecolor (since the score is drawn with the players).

Michael




0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users