Actions

SCHG How-to

Work with Objects

From Sonic Retro

Revision as of 22:14, 4 June 2010 by Ravenfreak (talk | contribs) (Section A: Movement)

(Original guide by Malevolence and MarkeyJester)

I decided I wanted to start some tutorials on objects because there aren't too many hacks with new objects. I figure I can start off with the really basic things then move on to more intricate subjects such as bosses. For now here is the first tutorial on very basic object concepts:

Lesson 1: The Basic Object

Section A: Overview

So, you want to start creating objects using the sonic engine? (We'll be using S1's but most of the ideas cover S1 and S2) Let's see firstly how most objects start out. I'll be using Hivebrain's 2005 disassembly for these examples:

<asm>; ---------------------------------------------------------------------------

Object 18 - platforms (GHZ, SYZ, SLZ)
---------------------------------------------------------------------------

Obj18: ; XREF: Obj_Index moveq #0,d0 move.b $24(a0),d0 move.w Obj18_Index(pc,d0.w),d1 jmp Obj18_Index(pc,d1.w)</asm>

First you see a little description of the object (commented out of course), followed by the object's code itself. It starts off by making sure there is currently no value in d0. After that it puts the value of the routine counter into d0 (whatever's in $24(a0) is in d0 such as 0, 2, 4, ect...). From there it moves the index's value with the value of d0 into d1, followed by jumping to the correct routine based on d1.

Section B: Routines

Routines are used to organize where certain code is and to be able to branch to those sections easily. Most objects used $24(a0) as the main routine counter and $25 as a secondary, but any scratch ram (ram not used by the object) can be used as a routine counter. The value in the routine counter needs to be even in order to work.

<asm>;=========================================================================== Obj18_Index: dc.w Obj18_Main-Obj18_Index; Jumped to if #0 is in $24(a0) dc.w Obj18_Solid-Obj18_Index; Jumped to if #2 is in $24(a0) dc.w Obj18_Action2-Obj18_Index; Jumped to if #4 is in $24(a0) dc.w Obj18_Delete-Obj18_Index; Jumped to if #6 is in $24(a0) dc.w Obj18_Action-Obj18_Index; Jumped to if #8 is in $24(a0)

===========================================================================</asm>


So, when this section is run (as soon as the object is loaded):

<asm>Obj18_Main: ; XREF: Obj18_Index addq.b #2,$24(a0)</asm>

When it gets to the next rts instead of going to Obj18_Main again, it will skip over that code and go to Obj18_Solid. Be warned if you put an odd value into the routine counter it won't work properly or if you put a number greater then the amount of routines, your game will crash.

Section C: Displaying/Basic SSTs

Alright, so you know the basis of routines, now where should we go? Well, some of the basic building blocks in objects are what are called SSTs. Every object in S1 and S2 is allotted $40 bytes of RAM to do whatever they want with, but some SSTs are already used for certain things (the game engine checks a few of the object's SSTs once each frame). Let's see an example:

<asm>Obj18_Main: ; XREF: Obj18_Index addq.b #2,$24(a0) ; adds to the routine so this isn't run again move.w #$4000,2(a0); moves #$4000 to the art tile's SST (it's 2 in S1 and S2) move.l #Map_obj18,4(a0); moves the mappings into the mapping's SST move.b #$20,$19(a0) ; width of object in pixels cmpi.b #4,($FFFFFE10).w ; check if level is SYZ bne.s Obj18_NotSYZ move.l #Map_obj18a,4(a0) ; SYZ specific code (use different mappings) move.b #$20,$19(a0) ; this really isn't needed since $19(a0) already has #$20 in it from the code before</asm>

What this basically does is define the width of the object in pixels and loads the starting art tile and palette and mappings. Down more in the code you'll see:

<asm>Obj18_NotSLZ: move.b #4,1(a0); use screen coordinates (such as the ones you see in debug mode) move.b #4,$18(a0); set priority (if other objects have a priority of a number less then 4 then the other object will be seen over this one if they interact) move.w $C(a0),$2C(a0); store a copy of the y coordinate ($C(a0) is y coordingate in S1 and S2 and $2C(a0) is scratch ram) move.w $C(a0),$34(a0); store another copy of the y coordinate move.w 8(a0),$32(a0); store a copy of the x coordinate move.w #$80,$26(a0); move #$80 into $26(a0) (to be used later)</asm>

This is a continuation of the loading of the object (the priority and using screen coordinates) and it saves the x and y pos and a value which will be used later. All you need to do to display an object it to fill in 1(a0), 2(a0), 4(a0) and jump to DisplaySprite.


Lesson 2: More Intricate Things With Objects

Section A: Movement

Welcome to lesson 2 of my object tutorials, in this section we're going to talk about other things that can be done with objects. So say you have an object displaying now and would like it to move. What you'll have to do is set its X and/or Y speed which are the SSTs $10(a0) and $12(a0) consecutively and then simply call a SpeedtoPos (or ObjectMove in S2)

<asm> move.w #-$40,$10(a0) ; make the object have a speed which moves it to the left slowly move.w #$400,$12(a0) ; make the object have a speed which moves it down quickly jmp SpeedToPos ; update the object's position (move the object)</asm>

If SpeedToPos is not called the object will stay immobilized. Also as a note, if you have a positive speed in the Y speed SST, the object will move down and if you have a negative speed in the Y speed SST, the object will move up. This is just a simple explanation of movement and I will eventually cover things such as how to make objects move in circular motions, but for now it's not necessary.

Section B: Timers

Another pretty basic idea used quite frequently is timers. It's what the GHZ boss uses to turn around and go back and forth. To use a timer what you'll have to do is take an unused SST and make sure it's set aside. In this example code, let's use $38(a0). What you want to do is somewhere before the timer starts (say the main loading code) where the code won't be used again is fill this with a number:

<asm>ObjXX_Main: move.w #$100,$38(a0)</asm>

When you come to an area where you want to start counting down, you'll want to set up a code that gets repeated until the time is up, as in:

<asm>ObjXX_CountDown: sub.w #1,$38(a0) ; subtracts from the timer beq.s ObjXX_Next ; tests if timer has hit 0 rts</asm>

Now, in ObjXX_Next you can increase the routine, change the speed/reverse it and you can reset the timer there as well so that it keeps changing speed:

<asm>ObjXX_Next: neg.w $10(a0) move.w #$100,$38(a0) rts</asm>

Lesson 3: Making Mappings

Ok, in this section we’re gonna cover the aspects of making object mappings, so what are mappings? Mappings are a way of presenting art tiles on an object in a certain way, now going back to “Section C: Displaying/Basic SSTs” in “Lesson 1” you’ll notice the line: <asm>move.l #Map_obj18,4(a0)  ; moves the mappings into the mapping's SST</asm> This loads the mappings under the routine name “Map_obj18”.

So, we need to make this routine and the mappings for it, now it doesn’t really matter where you put this but for now let’s put it directly under our object code (it just makes sense this way).

<asm>Map_obj18:</asm>

Next we need an index for this routine similar to the one explain in “Lesson 1”

<asm>Map_obj18: dc.w ObjectMap_01-Map_obj18 dc.w ObjectMap_02-Map_obj18 dc.w ObjectMap_03-Map_obj18 ObjectMap_01: dc.b $00

ObjectMap_02: dc.b $00

ObjectMap_03: dc.b $00</asm>

And now to make the mappings, you’ll notice three sections that look like “ObjectMap_01: dc.b $00” under this is where our set of mappings are going to go, now let me just set one out for you and explain what’s what:

<asm>ObjectMap_01: dc.b $01 dc.b $vv, $ww, $xx, $yy, $zz</asm>

So, you’ll notice that first of all the $00 that was previously there is now a $01, this is because a line has been added below it “dc.b $vv, $ww, $xx, $yy, $zz” that is one map block, if you had two of these below, then you would put $02 at the top there, that top value indicates the number of map blocks to use, we’re only gonna use one for now, so $01 it is.

Now to the actual mapping at hand, you notice I’ve put vv ww xx yy and zz, this is just to explain what each byte does, so lets take a look at vv, vv sets how many pixels up or down to present the tiles from where the object is, for example:

<asm>ObjectMap_01: dc.b $01 dc.b $08, $ww, $xx, $yy, $zz</asm>

This will move the tile mappings down 8 pixels from where the object is, so if the object is on the Y axis of $0200, the tile mappings will present on the Y axis of $0208, simple.

Next let’s skip over to zz, this is the same as vv except it’s on the X axiz (left or right) not the Y axiz, so: <asm>dc.b $vv, $ww, $xx, $yy, $08</asm>

This will move the tile mappings right 8 pixels from where the object is, so if the object is on the X axis of $0340, the tile mappings will present on the X axis of $0348, simple.

Ok, now to ww, this is how the tiles should present them selves, in other words how the block is made, now lets say we have 16 tiles in VRam (from 00 to 0F), depending on what code is in ww, will change how those 16 tiles are stacked on each other:

So if we put $00 the tiles will map:

00

If we put $01 the tiles will map:

00
01

If we put $02:

00
01
02

If we put $03:

00
01
02
03

If we put $04:

00 01


If we put $05:

00 02
01 03

If we put $06:

00 03
01 04
02 05

If we put $07:

00 04
01 05
02 06
03 07

If we put $08:

00 01 02

If we put $09:

00 02 04
01 03 05

If we put $0A:

00 03 06
01 04 07
02 05 08

If we put $0B:

00 04 08
01 05 09
02 06 0A
03 07 0B

If we put $0C:

00 01 02 03


If we put $0D:

00 02 04 06
01 03 05 07

If we put $0E:

00 03 06 09
01 04 07 0A
02 05 08 0B

If we put $0F:

00 04 08 0C
01 05 09 0D
02 06 0A 0E
03 07 0B 0F

That should be self explanatory for you: <asm>dc.b $vv, $ww, $xx, $yy, $zz</asm> Next let's look at xx, this sets if it is flipped, mirrored, and or uses a certain palette line, now it works like this:

First, you need to set if it’s flipped or mirrored, if you put 08 in, the mappings with appear mirrored, if you put 10 in, the mappings will appear flipped, if you put 18 in, the mappings will appear both mirrored and flipped.

Now, you need to set what colour palette line to use, so the first line would be 00, the second line would be 20, third is 40, and forth 60.

So now you need to add these 2 codes together, for example, if I want the mappings to be flipped (10) and use the second colour palette line (20), add them together, you get 30, and then put it where xx is.

Lets try another one, we want our mappings to be mirrored (08) and use the forth palette line (60), add them together, you get 68, and then put it where xx is, simple.

<asm>dc.b $vv, $ww, $xx, $yy, $zz</asm>

And finally, yy, this is what tile to start mapping from, so lets say we set ww at 03 so the tiles will position them selves like this:

00
01
02
03

If we put $02 in yy instead of $00, it’ll position the tiles like this:

02
03
04
05

As you may have noticed it mapped the tiles starting from tile number 02, instead of tile number 00, and there you have it, that’s how to map art on an object.