Advice for timing

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Advice for timing
by on (#183173)
I have some sprites I'm trying to control the speed of, or how often they move.

What I have done is place a counter in my NMI,

Code:
; increase frame counter
LDA frameCounter
CLC
ADC #$01
STA frameCounter


In my game logic, which is separate from my NMI code, I check if frameCounter is greater than 30 (or roughly half a second) and I move my sprites if that condition is true. I then reset the frame counter back to zero.

Is this a common approach to timing? I figure the NMI will fire roughly 60 times a second so I can piggyback off that?

In Allegro and SDL I would get the current time and compare if the elapsed time between the last movement was greater than some timing variable.
Re: Advice for timing
by on (#183176)
Yes, that's one way of indicating to the main thread that an NMI has occurred.

But there's a shortcut for the addition:
Code:
inc frameCounter


If your NMI handler is fairly simple, this also saves you from having to push and pull A so as not to disrupt the main thread's use of A. Many of my games so far have used just this sort of trivial NMI handler. You can see it in, for example, my NROM project template:
Code:
.proc nmi_handler
  inc nmis
  rti
.endproc


If you have more than one thing animating at once, you'll need to let frameCounter free-run without resetting it, and then treat frameCounter as you would retrace_count in Allegro: saving the value at the start of an interval and comparing it at the end of an interval.
Re: Advice for timing
by on (#183177)
You'll want to look at fixed-point addition, aka sub-pixel precision. It's explained pretty well in this thread:
https://forums.nesdev.com/viewtopic.php?f=10&t=12125

I don't know how often a timer-based approach is used for movement, but I can say that I've used it before in my earlier programs and it was ugly and inflexible. Using sub-pixel positioning (with 16 bits for movement rates and positions, where only the upper 8 bits are full pixels) is much cleaner.
Re: Advice for timing
by on (#183179)
like tepples said, I think it's best to not reset the NMI counter variable in main code.

2 ways to implement a half second timer is to have a variable that decrements towards 0 each frame in your main code, or to add 30 with the NMI counter into a different variable and compare that every frame.
Code:
; setup
lda #30
sta timer_1

; for every frame in your main loop
dec timer_1
bne timer_1_not_triggered
  ; do action
timer_1_not_triggered:


This second method is different in that even if the main loop lags, the timer will still fire in at least 30 frames, but it has the disadvantage of having half the range due to using bmi to account for the frameCounter overshooting from lag.
Code:
; setup
clc
lda frameCounter
adc #30
sta timer_2

; for every frame in your main loop
lda frameCounter
cmp timer_2
bmi timer_2_not_triggered
  ; do action
timer_2_not_triggered:
Re: Advice for timing
by on (#183180)
Memblers wrote:
I don't know how often a timer-based approach is used for movement

Thwaite and RHDE use a 6-frame timer for tiny sprites that need to move only once every tenth of a second or less often. The units in both games move once every 0.2 seconds. Machine guns in several games use a frame count to tell how often to fire. Counting frames between moves is also commonly used for cursor movement and in falling block games for sideways piece movement, both of which behave similarly to keyboard autorepeat.

But for things that change continuously, such as the position of a character in a platformer, I agree that fixed-point arithmetic is the best choice.
Re: Advice for timing
by on (#183194)
If the numbers involved are powers of two, like 2, 4, 8, 16, etc. as an alternative to storing individual states you can just apply bitwise operations to the free-running frameCounter variable you have.

For example, if you have 4 frames of animation, and want to switch every 8 frames:

Code:
lda frameCounter
lsr
lsr
lsr ; A = frameCounter / 8
and #3 ; A = A % 4
; A is now either 0,1,2,3 and will change every 8 frames.

I don't do this for most of my objects, because I usually want different timings besides ones that line up with 4/8/16 etc. but I still find it's a useful alternative in some cases.

casprog wrote:
Is this a common approach to timing? I figure the NMI will fire roughly 60 times a second so I can piggyback off that?

In Allegro and SDL I would get the current time and compare if the elapsed time between the last movement was greater than some timing variable.

It is a very common approach to count frames in some way, and base your animation timings off of that, yes.

This is often applicable to modern games, too, if they have a fixed framerate design (e.g. fighting games almost always do this). Choosing a fixed vs. variable framerate makes a big impact on a game's design, but with NES normally a fixed framerate approach is presumed.