Moving sprites in a sine wave pattern.

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Moving sprites in a sine wave pattern.
by on (#167024)
I am trying to get an effect with sprites moving in a sine wave pattern similar to what happens in wamma's quantumdisco brothers demo at about 1:10

https://www.youtube.com/watch?v=hhoa_K75BKI

It is vaguely NSFW as there is low res naked person in the background.

I ASSUME that the sprites are all grouped as a large meta sprite which controls the movement of the whole spiral of sprites back and forth across the screen. I also guess the individual sprites are following the "control" sprite with a movement offset of some sort.

I have a little proof of concept rom where I have 4 sprites defined as a meta sprite moving right and left which is good. They are also mapped with an offset which is also great. The problem is they are moving as one static sprite rather than individually and I am not sure why.

Would anyone be able to point me in the right direction? I think this code is probably the problem. Sorry it is a bit long.

Code:
;-----movesprites---------------------------------------------
movemetasprite:

movemetaspriteright:
  LDA metaspriteright
  BEQ movemetasprightrightdone         ;if metaspriteright=0, skip this section

  LDA sprite_RAM+3
  CLC
  ADC metaspritespeedx            ;sprite_RAM position = sprite_RAM + metaspritespeedx
  STA sprite_RAM+3

  LDA sprite_RAM+3
  CMP #rightside
  BCC movemetasprightrightdone            ;if sprite_RAM < rightside, still on screen, skip next section
  LDA #$00
  STA metaspriteright
  LDA #$01
  STA metaspriteleft                  ;bounce, metasprite now moving left
movemetasprightrightdone:


movemetaspriteleft:
  LDA metaspriteleft
  BEQ movemetaspriteleftdone   ;;if metaspriteleft=0, skip this section

  LDA sprite_RAM+3
  SEC
  SBC metaspritespeedx       
  STA sprite_RAM+3

  LDA sprite_RAM+3
  CMP #leftside
  BCS movemetaspriteleftdone     
  LDA #$01
  STA metaspriteright
  LDA #$00
  STA metaspriteleft         ;;bounce, now moving right

movemetaspriteleftdone:
RTS


;-----updatesprites-------------------------------
updatesprites:

   LDA sprite_RAM            ;vertical updates
   CLC
   ADC #$8
   STA sprite_RAM+4
   CLC
   ADC #$8
   STA sprite_RAM+8
   CLC
   ADC #$8
   STA sprite_RAM+12

   LDA sprite_RAM+3             ;horizontal updates
   STA sprite_RAM+7
   STA sprite_RAM+11
   STA sprite_RAM+15

rts


;------------------------------------


;-----offset--------------------------------------
offsetmetasprite:
   LDA sprite_RAM+3             ;horizontal updates
   CLC
   ADC #$00
   STA sprite_RAM+3

   LDA sprite_RAM+7             ;horizontal updates
   CLC
   ADC #$01
   STA sprite_RAM+7

   LDA sprite_RAM+11            ;horizontal updates
   CLC
   ADC #$02
   STA sprite_RAM+11

   LDA sprite_RAM+15            ;horizontal updates
   CLC
   ADC #$03
   STA sprite_RAM+15
RTS
Re: Moving sprites in a sine wave pattern.
by on (#167025)
You want a sine wave movement?

I'd go with a very large LUT (array) of precalculated x,y positions.
Re: Moving sprites in a sine wave pattern.
by on (#167026)
Or just do what Super Mario Bros. does for its vertical and horizontal lifts: make the acceleration proportional to the displacement from center. This represents a second-order linear differential equation whose solution is a sinusoid:

d²y/dt² = -ky, k > 0

Wolfram Alpha solves it as

y = a cos(√(k)t) + b sin(√(k)t)

where a and b depend on the initial position and velocity.

This can be implemented with X displacement, Y displacement, X velocity, and Y velocity variables. Do you want someone to walk you through it?
Re: Moving sprites in a sine wave pattern.
by on (#167053)
Sine tables used to be very common in the old days (especially for raycasters). They don't even need to be very large. lda SineTable,y where Y is the position along X. The table itself can be of any size depending on what you're doing with it. Should be stored in ROM not RAM, naturally. Even 256 is too large (unless you only want one giant wave), because sine repeats so you only need a very small section of the wave. The Y values in the table correspond to pixels on screen, and same with the value used to index the table.
Re: Moving sprites in a sine wave pattern.
by on (#167054)
tepples wrote:
acceleration proportional to the displacement from center

This creates an ideal spring, though it is not easy to control the speed/period of the waveform. You need to do fractional updates, or have some ability to multiply to accomplish that, which can be tricky to do efficiently enough on the NES.


An alternative, used in a lot of games (e.g. metroid's waver), is just to alternate a fixed acceleration value, negative and positive, with a regular interval. i.e. the acceleration is a square wave, which produces a triangle wave in the velocity (i.e. velocity linearly increases and decreases), and then finally the position is a nice soft curve. It's not a true sine wave, but it might look close enough for your purposes.

In this alternative, it's easy to control period because the resulting wave has the same period as your acceleration. Similarly, it's straightforward to control amplitude by just adjusting the acceleration value. Since position isn't feeding back into the acceleration like with the spring, enemies can be pushed around, or deal with collision easily without causing them to go wild.


In both the spring, and this alternative pseudo-spring it's a little difficult to start anywhere in the phase, the spring is easy to start at the peak (i.e. velocity = 0, position = peak), but everywhere else requires you to do some calculation to figure out the appropriate velocity and position at that phase. The alternative pseudo-spring is even trickier, like it can start at position=0 easily but you need to calculate velocity/position carefully to start anywhere else.


If you're doing 3D rotation, though, probably a sine table is the most appropriate. Very easy to just select any phase you want.
Re: Moving sprites in a sine wave pattern.
by on (#167056)
Pre-generated data table for the sine wave data (X/Y coordinate offsets or whatever you wish -- it's gonna vary depending on what you want) is, IMO, the way to do this. The manual calculate-it-yourself-through-painful(IMO)-math-in-realtime isn't worth it. Just giving my two cents.
Re: Moving sprites in a sine wave pattern.
by on (#167141)
koitsu wrote:
Pre-generated data table for the sine wave data (X/Y coordinate offsets or whatever you wish -- it's gonna vary depending on what you want) is, IMO, the way to do this. The manual calculate-it-yourself-through-painful(IMO)-math-in-realtime isn't worth it. Just giving my two cents.


Thanks everyone for their input. I went for the sine wave and got a movement style I am fairly happy with.

Code:

movesprites:
   LDA sine,x
   STA sprite_RAM+3
   INX
RTS

sine:
.db $11,$12,$13,$14, $16,$18,$1a,$1c, $1f,$22,$25,$28, $2c,$30,$34,$38
.db $3c,$40,$44,$48, $4b,$4e,$51,$54, $56,$58,$5a,$5c, $5d,$5e,$5f,$60
.db $60,$5f,$5e,$5d, $5c,$5a,$58,$56, $54,$51,$4e,$4b, $48,$44,$40,$3c
.db $38,$34,$30,$2c, $28,$25,$22,$1f, $1c,$1a,$18,$16, $14,$13,$12,$11


This works great for a single sprite. If I want to add more sprites I was thinking of doing

Code:
   

LDA sine,x
STA sprite_RAM+3
INX
STA sprite_RAM+7
INX

---add more sprites here---

RTS


But this seems to speed up the movement to a kind of crazy level. Is there a better way of adding more sprites?

Thanks for all the help by the way!
Re: Moving sprites in a sine wave pattern.
by on (#167145)
lazerbeat wrote:
But this seems to speed up the movement to a kind of crazy level.

It sounds like you're calling that function using whatever value was in X when the function returned. Since you increment X once for each sprite you display, X is incrementing by the number of sprites every frame.

What you want is a variable to track which step in the sine you should be using for each frame. Load that variable into X before calling the function to display the sprites. What goes into that variable is up to you, but for starters, try incrementing that variable once per frame.
Re: Moving sprites in a sine wave pattern.
by on (#167148)
Joe wrote:
What you want is a variable to track which step in the sine you should be using for each frame. Load that variable into X before calling the function to display the sprites. What goes into that variable is up to you, but for starters, try incrementing that variable once per frame.


So sill of me. That is exactly what I needed. This is what I am using

Code:
LDX framenumber
LDA sine,x
STA sprite_RAM+3
inx
LDA sine,x
STA sprite_RAM+7
inc framenumber
RTS


Works perfectly thanks so much! I will start making the code a bit less hideous now.