How to make an sprite move slowly?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
yaofan1212
Posts: 12
Joined: Sat Dec 04, 2021 9:48 am

How to make an sprite move slowly?

Post by yaofan1212 »

Hello.

How to make an sprite to move slowly?

The minimum speed I can move an sprite is the speed I get when I add 1 to the position of the sprite. This is still too quick. How is this done? Let pass some vblanks an then update? this technique has a name?

Thanks.
lidnariq
Posts: 11430
Joined: Sun Apr 13, 2008 11:12 am

Re: How to make an sprite move slowly?

Post by lidnariq »

Traditionally, you store the position as a fixed-point number, for example "8.8" which means 8 bits to the left of the decimal point and 8 bits to the right.

Mathematically, it looks identical to working with 16-bit numbers for addition and subtraction - you just use the upper byte instead of the lower byte when it comes to displaying it on screen.

So a speed of $00.80 would result in a speed of one-half-pixel per refresh, a speed of $00.40 in one-quarter, and so on.
User avatar
Dwedit
Posts: 4922
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: How to make an sprite move slowly?

Post by Dwedit »

Two ways to do this:

Way 1: Fixed-Point Arithmetic
Way 2: Move every X frames

I'd recommend Fixed-point arithmetic because it's very flexible (use any fraction you want) and simple once you understand how it works.

'Fixed point arithmetic' means you have a Fractional part to your number. You treat this fractional part as a number divided by 256. 128 (0x80) means 128/256 (1/2), 64 (0x80) means 64/256 (1/4), 86/256 is close to 1/3, etc.
You do 16-bit math when you want to add or subtract a fractional number, then discard the low 8 bits when you actually want to display the sprite's position.

Alternative: moving every X frames

Way one: A global frame counter, then using AND
This is really easy. You have a value somewhere that increases every frame.
If you AND that value with 1, then when the result is 0, you have a condition for every other frame.
If you AND that value with 3 then result is zero, you get every 4th frame, AND with 7 then result is zero to get every 8th frame, and so on.

Way two: Use a counter that ticks down, when it reaches 0, you can move it.
Last edited by Dwedit on Sun Dec 05, 2021 11:43 pm, edited 1 time in total.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
yaofan1212
Posts: 12
Joined: Sat Dec 04, 2021 9:48 am

Re: How to make an sprite move slowly?

Post by yaofan1212 »

Dwedit wrote: Sat Dec 04, 2021 10:59 am Two ways to do this:

Way 1: Fixed-Point Arithmetic
Way 2: Move every X frames

I'd recommend Fixed-point arithmetic because it's very flexible (use any fraction you want) and simple once you understand how it works.

'Fixed point arithmetic' means you have a Fractional part to your number. You treat this fractional part is like a number divided by 256. 128 (0x80) means 128/256 (1/2), 64 (0x80) means 64/256 (1/4), 86/256 is close to 1/3, etc.
You do 16-bit math when you want to add or subtract a fractional number, then discard the low 8 bits when you actually want to display the sprite's position.

Alternative: moving every X frames

Way one: A global frame counter, then using AND
This is really easy. You have a value somewhere that increases every frame.
If you AND that value with 1, then when the result is 0, you have a condition for every other frame.
If you AND that value with 3 then result is zero, you get every 4th frame, AND with 7 then result is zero to get every 8th frame, and so on.

Way two: Use a counter that ticks down, when it reaches 0, you can move it.
If I understood correctly, do you mean to do some decimal math to pass by cpu cycles?

Something like:

accumulator = 0.3
accumulator = accumulator +0.3

after the 4 vblank cycle we should have 1.2 which is trunked to 1?

In this way, after 4 blanks, the sprite moved 1 pixel?
User avatar
Dwedit
Posts: 4922
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: How to make an sprite move slowly?

Post by Dwedit »

It's like "decimal" in that there is a fractional number involved, but it's not like decimal in that it's not base 10. It's a fraction divided by 256. So 0.3 would approximate to 77/256, or 0.30078125. (Which is very close to 0.3)

It's a 16-bit addition operation. During this addition, the Integer part of the number is the high byte, and your fractional part is the low byte. You add 77 to the low byte, then add 0 with carry to the high byte.

After adding 77 four times, you get 0x01 in the high byte, and 0x34 (52) in the low byte. Your result is 1 + 52/256, or 1.203125.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
yaofan1212
Posts: 12
Joined: Sat Dec 04, 2021 9:48 am

Re: How to make an sprite move slowly?

Post by yaofan1212 »

Dwedit wrote: Sat Dec 04, 2021 2:30 pm It's like "decimal" in that there is a fractional number involved, but it's not like decimal in that it's not base 10. It's a fraction divided by 256. So 0.3 would approximate to 77/256, or 0.30078125. (Which is very close to 0.3)

It's a 16-bit addition operation. During this addition, the Integer part of the number is the high byte, and your fractional part is the low byte. You add 77 to the low byte, then add 0 with carry to the high byte.

After adding 77 four times, you get 0x01 in the high byte, and 0x34 (52) in the low byte. Your result is 1 + 52/256, or 1.203125.
Yes I understand thanks. But the idea is that in this way is possible to let pass some CPU cycles till get 1? So, after 3 vblanks one get 1 and then the sprites moves?
yaofan1212
Posts: 12
Joined: Sat Dec 04, 2021 9:48 am

Re: How to make an sprite move slowly?

Post by yaofan1212 »

Thanks for your help. I was able to slow the sprite bypassing cycles just with a loop using integers.

What I did was to make to set a memory byte as a global variable ($15). Then, for each NMI cycle I add 1 to this global variable. Then compare this value with a predefined one, like 5. If true, move the sprite 1 pixel.

Code: Select all

 LDA #00
  LDX  #00    
  LDA  $15    
  CMP  #5     
  BNE  DONE
  LDA $0200       
  CLC             
  SBC #$01     
  STA $0200
  ldx #0
  stx $15
  jmp JJ
DONE : ADC #1
  STA $15
JJ: 
Image
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: How to make an sprite move slowly?

Post by Pokun »

Looks like you are doing the "Way 2: Move every X frames" method (every 5 frames in your case).

If you need smoother movements you should look into the Fixed-Point Arithmetic method, it's really very simple and much more versatile.
As Lidnariq and Dwedit both said, you store your position as a 16-bit number and only the high byte is the real pixel position while the low byte is like an imaginary sub-pixel position used to increment the real pixel position at a slower rate. Each time the sub-pixel position overflows/underflows, the object will move 1 real pixel. You really only need to know how to handle 16-bit math (using carry).

Example:

Code: Select all

player_x equ $00      ;player X-position
player_x_sub equ $01  ;player X-sub-position

;If RIGHT-button is pressed:
  lda player_x_sub
  clc
  adc #$40
  sta player_x_sub   ;add $40 subpixels to position
  lda player_x
  adc #$00
  sta player_x       ;add 0 pixels to position (+carry)
  
  ...
  
;Update sprites:
  lda player_x
  sta $0203    ;store X position to OAM to move sprite
If I got it right, this should move the player $00.40 pixels per frame (1 pixel every 4th frame). You can set movement speed to be anything between $00.01 (extremely slow) and $00.FF (almost the same as 1 pixel per frame), or something like $02.80 (2 and a half pixel per frame) for fast moving objects for example. The range with a 16-bit value like this is very fine-grained and probably more than smooth enough for all your needs.
yaofan1212
Posts: 12
Joined: Sat Dec 04, 2021 9:48 am

Re: How to make an sprite move slowly?

Post by yaofan1212 »

Pokun wrote: Wed Dec 08, 2021 5:06 pm Looks like you are doing the "Way 2: Move every X frames" method (every 5 frames in your case).

If you need smoother movements you should look into the Fixed-Point Arithmetic method, it's really very simple and much more versatile.
As Lidnariq and Dwedit both said, you store your position as a 16-bit number and only the high byte is the real pixel position while the low byte is like an imaginary sub-pixel position used to increment the real pixel position at a slower rate. Each time the sub-pixel position overflows/underflows, the object will move 1 real pixel. You really only need to know how to handle 16-bit math (using carry).

Example:

Code: Select all

player_x equ $00      ;player X-position
player_x_sub equ $01  ;player X-sub-position

;If RIGHT-button is pressed:
  lda player_x_sub
  clc
  adc #$40
  sta player_x_sub   ;add $40 subpixels to position
  lda player_x
  adc #$00
  sta player_x       ;add 0 pixels to position (+carry)
  
  ...
  
;Update sprites:
  lda player_x
  sta $0203    ;store X position to OAM to move sprite
If I got it right, this should move the player $00.40 pixels per frame (1 pixel every 4th frame). You can set movement speed to be anything between $00.01 (extremely slow) and $00.FF (almost the same as 1 pixel per frame), or something like $02.80 (2 and a half pixel per frame) for fast moving objects for example. The range with a 16-bit value like this is very fine-grained and probably more than smooth enough for all your needs.
Thank you! Your explanation is much clearer. To tell you the truth, I didn't understood the idea using fixed point given by the above experts.

Just to be sure, what I understood is to increment the low byte in each NMI iteration by a defined constant, eventually it will overflow and when this occurs the high byte is incremented by another constant?
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: How to make an sprite move slowly?

Post by Pokun »

I figured you needed a concrete example.

No you don't need to increment the high byte separately. It should be taken care of by the carry in the 16-bit addition operation that I did in the example, it's incremented by 1 whenever the low byte overflows (carry is set). I'm not sure if you are you familiar with 16-bit math on the system? If not, it's the same as 8-bit math, you just add the two low bytes and the two high bytes together separately, but you don't clear carry (CLC) before you add the high bytes together. Likewise with 16-bit subtraction you don't set carry (SEC) before subtracting the two high bytes.

Code: Select all

;Adding the two 16-bit values num1 and num2 together:
  lda num1_lo   ;load low part of num1
  clc           ;make sure carry is clear
  adc num2_lo   ;add low part of num2 to low part of num1
  sta res_lo    ;store low part of result
  lda num1_hi   ;load high part of num1
  adc num2_hi   ;add high part of num2 plus carry to high part of num1
  sta res_hi    ;store high part of result
For subtraction you do the exact same thing but with SEC and SBC instead of CLC and ADC.


I'm not sure what you mean by incrementing it in the NMI. You increment the value wherever you have your game logic code (which could be in NMI) whenever the player is pressing RIGHT in my example. But yeah you could increment it every frame to have it move all the time just for testing and of course enemies and similar objects would need some logic that each frame determines whether or not they should move and in what direction etc.

Also you may want to change the literals for symbolic constants or variables (RAM registers) so that movement speed can be changed. As I said earlier both the high byte and low byte can be incremented, just in my example the high byte is $00 since we wanted to move slower than 1 pixel/frame. You must make sure to add the high byte even if it is 0, otherwise an eventual carry won't be added and it simply won't work.
yaofan1212
Posts: 12
Joined: Sat Dec 04, 2021 9:48 am

Re: How to make an sprite move slowly?

Post by yaofan1212 »

Thanks Mr. Pokun. It is very clear now!
Post Reply