Actually, it's extremely simplistic, it only requires to find a good equation, use your favorite spreadsheet software and modify a few details in the code.
The idea is to use some pre-computed data stored in a simple array rather than do useless calculations (that would always lead to the exact same result). This way, you get rid of a waste of hundreds of processor cycles. So this is also a simple example of how to use an array.
THE CODE
Here's the original lost ring code (from the svn), it's Obj37 in Hivebrain's disassembly:
RingLoss: ; XREF: Obj_Index
moveq #0,d0
move.b obRoutine(a0),d0
move.w RLoss_Index(pc,d0.w),d1
jmp RLoss_Index(pc,d1.w)
; ===========================================================================
RLoss_Index: dc.w RLoss_Count-RLoss_Index
dc.w RLoss_Bounce-RLoss_Index
dc.w RLoss_Collect-RLoss_Index
dc.w RLoss_Sparkle-RLoss_Index
dc.w RLoss_Delete-RLoss_Index
; ===========================================================================
RLoss_Count: ; Routine 0
movea.l a0,a1
moveq #0,d5
move.w (v_rings).w,d5 ; check number of rings you have
moveq #32,d0
cmp.w d0,d5 ; do you have 32 or more?
bcs.s @belowmax ; if not, branch
move.w d0,d5 ; if yes, set d5 to 32
@belowmax:
subq.w #1,d5
move.w #$288,d4 ; << THIS LINE (1)
bra.s @makerings
; ===========================================================================
@loop:
bsr.w FindFreeObj
bne.w @resetcounter
@makerings:
move.b #id_RingLoss,0(a1) ; load bouncing ring object
addq.b #2,obRoutine(a1)
move.b #8,obHeight(a1)
move.b #8,obWidth(a1)
move.w obX(a0),obX(a1)
move.w obY(a0),obY(a1)
move.l #Map_Ring,obMap(a1)
move.w #$27B2,obGfx(a1)
move.b #4,obRender(a1)
move.b #3,obPriority(a1)
move.b #$47,obColType(a1)
move.b #8,obActWid(a1)
move.b #-1,(v_ani3_time).w ; this is "move.b #-1,($FFFFFFA6).w" in Hivebrain's disassembly
tst.w d4 ; << THIS LINE (2)
bmi.s @loc_9D62 ; << THIS LINE (2)
move.w d4,d0 ; << THIS LINE (2)
bsr.w CalcSine ; << THIS LINE (2)
move.w d4,d2 ; << THIS LINE (2)
lsr.w #8,d2 ; << THIS LINE (2)
asl.w d2,d0 ; << THIS LINE (2)
asl.w d2,d1 ; << THIS LINE (2)
move.w d0,d2 ; << THIS LINE (2)
move.w d1,d3 ; << THIS LINE (2)
addi.b #$10,d4 ; << THIS LINE (2)
bcc.s @loc_9D62 ; << THIS LINE (2)
subi.w #$80,d4 ; << THIS LINE (2)
bcc.s @loc_9D62 ; << THIS LINE (2)
move.w #$288,d4 ; << THIS LINE (2)
@loc_9D62: ; << THIS LINE (2)
move.w d2,obVelX(a1) ; << THIS LINE (2)
move.w d3,obVelY(a1) ; << THIS LINE (2)
neg.w d2 ; << THIS LINE (2)
neg.w d4 ; << THIS LINE (2)
dbf d5,@loop ; repeat for number of rings (max 31)
@resetcounter:
move.w #0,(v_rings).w ; reset number of rings to zero
move.b #$80,(f_ringcount).w ; update ring counter
move.b #0,(v_lifecount).w
sfx sfx_RingLoss ; play ring loss sound
RLoss_Bounce: ; Routine 2
move.b (v_ani3_frame).w,obFrame(a0)
bsr.w SpeedToPos
addi.w #$18,obVelY(a0)
bmi.s @chkdel
move.b (v_vbla_byte).w,d0
add.b d7,d0
andi.b #3,d0
bne.s @chkdel
jsr ObjFloorDist
tst.w d1
bpl.s @chkdel
add.w d1,obY(a0)
move.w obVelY(a0),d0
asr.w #2,d0
sub.w d0,obVelY(a0)
neg.w obVelY(a0)
@chkdel:
tst.b (v_ani3_time).w
beq.s RLoss_Delete
move.w (v_limitbtm2).w,d0
addi.w #$E0,d0
cmp.w obY(a0),d0 ; has object moved below level boundary?
bcs.s RLoss_Delete ; if yes, branch
bra.w DisplaySprite
; ===========================================================================
RLoss_Collect: ; Routine 4
addq.b #2,obRoutine(a0)
move.b #0,obColType(a0)
move.b #1,obPriority(a0)
bsr.w CollectRing
RLoss_Sparkle: ; Routine 6
lea (Ani_Ring).l,a1
bsr.w AnimateSprite
bra.w DisplaySprite
; ===========================================================================
RLoss_Delete: ; Routine 8
bra.w DeleteObject
The lines with "<< THIS LINE" as a comment are the ones we're going to change.
Let's do this first:
Change the line marked with a "(1)" with:
lea SpillingRingData,a3 ; load the address of the array in a3
You can choose another name, of course.
And change all the lines marked with a "(2)" with this code:
move.w (a3)+,$12(a1) ; move the data contained in the array to the y velocity and increment the address in a3
move.w (a3)+,$10(a1) ; move the data contained in the array to the x velocity and increment the address in a3
That's it for the asm part. Short, isn't it?
THE DATA
Open the spreadsheet software of your choice.
- In a first column, put 0 to "n" (32 in the original game).
- In a second column, you have to set the absolute speed of the rings. The maximum absolute speed should be approximately -1000 to -1200 (not in hex).It's negative because of the coordinate system of Sonic.
You can use either all the same values, or different ones (eg: -1200, -800, -1150, -775...)
- In the third column, you have to put the angle equation:
The parameters should be something like this:
a=pi/2+b/t*n+c
Where: "a" is the angle,
"b" is the total angle covered by your rings (a full circle is 2*pi),
"t" is the maximum number of rings that will appear,
"n" is the number of the current ring,
"c" is a bias, 0 if you want your first ring to move vertically, another value if you want it to go left/right.
- In the fourth column, you'll put the vertical speed:
v=sin(a)*s
Where: "v" is the vertical speed
"a" is the angle (third column)
"s" is the absolute speed of the ring (second column)
- In the fifth column, you'll put the horizontal speed:
h=cos(a)*s
Where: "h" is the horizontal speed
"a" is the angle (third column)
"s" is the absolute speed of the ring (second column)
Now, setup the 4th and 5th columns to show 0 decimals. Copy their content in your text editor, and arrange things so it looks like this:
SpillingRingData:
dc.w -1100, 0
dc.w -847, -67
dc.w -847, 67
dc.w -1086, -174
; (...)
; (...)
; (...)
even
You don't have to turn the decimal values into hexadecimal values, the assembler will do it for you.
Make sure you have as many lines as the maximum amount of rings you can lose.
If you want your rings to got in both directions, make lines for only half of the rings, duplicate all lines (excepted the purely vertical ones) and negate the horizontal speed.
You'll only have to change the absolute speeds and the total angle to design tons of great patterns, and with a few more changes you can even have several patterns in the same game.
Put your new array wherever you want in your code (for example before or after the object).
Enjoy!
Edited by OrdosAlpha, 29 April 2012 - 02:36 PM.













