Programming A Sprite - (Part 1)
The reason I made this thread (tutorial?) was because many people find mikeyk's tutorial a bit too complicated or vague. This tutorial goes in-depth on how to program custom sprites to use with sprite tool. It covers how to make a simple static sprite, and moving sprite, an animated sprite, a large (32x32) sprite and finally, a large moving sprite with many features (let's just call this a boss). Of course, it's not the best and perfect tutorial you'll find as people like Roy/Ersanio probably know a lot more than me, but hey.. I've coded many sprites including a few bosses so this tutorial should just be enough for one to code several sprites.
Before actually starting though, you'll obviously need a bit of ASM knowledge. Enough knowledge to create simple blocks won't really help though.. you should have atleast these chapters covered:
- Loading/Storing (LDA and STA)
- Conditional Branching (CMP, BEQ, BCC, BCS, BNE, BRA)
- Math Commands (INC, DEC, CLC, ADC, SEC, SBC, ASL, LSR)
- The Stack (PHA, PHX, PHY, PLA, PLX, PLY)
- The X and Y Registers (LDX, LDY, STX, STY, INX, INY, CPX, CPY)
- Transfering (TAX, TAY, TXY, TYX, TYA, TXA)
- Bitwise Operations (AND, ORA, EOR)
- Jumping (JSL, JSR)
- The Data Bank (PHB, PHK, PLB) (This is not necessarily required, it is only added at the beginning of a sprite code.)
And of course, you should also be patient to learn. If you're lazy or uninterested in making sprites, this tutorial isn't for you
Getting Started - A Generator
Before we actually start coding sprites, it would be best to start off with generators instead. Not only are they simpler to start off with, but it's also something reasonable to start off before moving on to something a bit more advanced.
Firstly, you should know how a generator works. A generator is basically a type of sprite inserted through Sprite Tool from values D0-DF. Whatever code you write in a generator will always
be executed if Mario has reached the screen number the generator is on. Generators do NOT have any graphics, nor can they attack/move like sprites do. As a matter of fact, they work very different from sprites. The code in a generate is also run every frame
. But as they are still a type of sprite, we'll cover them. By the end of this lesson, we will make a generator that
crashes the game
will play a sound effect and give Mario a fire flower, on the condition that he has atleast 50 coins and 3 lives. If he doesn't it will hurt him. This may sound a bit tough to make, but believe me, it's pretty simple once you've learnt it.
For all type of sprites, whether it's a normal sprite, a generator or a shooter, all of them 2 main routines
The Initialisation Routine:
The initialisation routine in a sprite is the code that happens once -and only once. Not more than that. It also comes before the main routine (obvious I guess).
Here initial means that the code happens once the sprite has come into existence. Once it has, this code will never be executed again.
Why is this useful though? It's pretty handy if you want your sprites to initially walk in the direction Mario is going, or set health points for a boss. Or maybe you might want to clear all RAM Address you want to use for later in the initial routine so nothing messes up. There are plenty of other ways to can utilise the initialisation routine.
The Main Routine:
The Main Routine is where you write your main sprite code. This routine is run every frame, being that will be executed all the time as long as the sprite is on screen. Your sprite movements, graphics, etc. are all written here.
A Static Sprite
Now you know what the initial (INIT)
and main (MAIN)
routine are. They're always used in sprites.
To start the INIT routine, you write:
This is an identifier for Sprite Tool to run the sprite's initial routine. It's in the same format as above - dcb
, followed by INIT
written in inverted speech.
To start the MAIN routine, you write:
This is an identifier for Sprite Tool to run the sprite's main routine. It's in the same format as above - dcb
, follwed by MAIN
written in inverted speech.
Now you know how to write and start the initial and main routine. For our first very simple sprite, we will make both of these codes return, so they do nothing at all:
So what we did was start the initial and main routine, and both of them simply return and do nothing. It's still technically a sprite, but it has no functions (yet).
One thing to note is that unless you access a subroutine through a JSR (E.g. JSR Code), sprite codes always end in an RTL
. If you ever write an RTS to end the INIT routine, your sprite will cause the game to crash once you test it.
A generator, unlike regular sprites, does NOT have an INIT routine. I also stated this above in bold. This does NOT mean that we only write dcb "MAIN" in our generator code though. All I mean here is that you cannot write any code in the INIT routine for a generator. If it's run every frame, why would you need to make it do something initially? Makes sense right?
So in a generator, you still write dcb "INIT". But because there is no initialisation code, we directly end it in an RTL. We cannot put any code there, like STZ $19
(make Mario small) in the INIT code because the code is run every frame (once Mario enters the screen the generator is on, of course) anyway.
Writing Some Code For The Generator
Now that we know where the initial and main codes are written in a generator, let's actually make some code for it. For a very simple generator, we'll it turn the coin counter to 00.
For this, we'll just need to create a code which makes Mario's coins 00 in the main code (not INIT - as I said, you can't write code in a generator's INIT routine). It's as simple as this:
It should be quite simple to understand. All I did in the main routine was add STZ $0DBF
, which clears Mario's RAM coins.
You can create more complex codes in a generator, as long as your code makes sense.
Now here's one more important thing to note: Once a generator's code is run, it will always be run unless you exit that level
. If suppose, I place the generator above at screen 05. I go to that screen and the coins are 00. However, even if I go to screen 0C in the same level and collect coins, the counter will still remain 00. This is because once a generator's code has started, it won't end until Mario enters a (sub)level without the generator near his entrance point.
Here's a slightly more complex code:
What that block does: If Mario has less than 10 coins, the code will simply return. If he does though, it'll make him get a cape (#$02 -> $19 = Caped Mario).
That's pretty much it for coding a generator. You write all of the code you want in the main routine, but still write the initial routine at the beginning of a file. There is NO limit to how many opcodes you can use or how big your generator file is. Just make sure that your code works.
The .CFG File!
So now you've made the .ASM file, but you can't test it because you need to insert a .cfg file, not just an .asm one!
Well, for a generator you don't actually edit the normal "cfg options" as you would in a sprite. Stuff like "Takes 5 fireballs to hit"
or "Can be jumped on"
are null in a generator; You can't really edit options for them.
Open your generators
folder in Sprite Tool. You must atleast see one .cfg file. For my example, there's generic.cfg
Make a copy of it, and rename the name of the .cfg file, preferably with the same name as the one with your .ASM code. Now you should have atleast 2 files:
<- This is the file containing your generator's main code.
<- This is the .cfg file for the generator, which is inserted with Sprite Tool.
However, all we've done is rename the .cfg file of an already existing one! That doesn't mean it'll execute the code from our .asm file, but generic.asm instead.
To make it execute the code from our file or "point
" to our code, open up the .cfg file with Notepad or any other text document editor.
This is what I find in my generator.cfg:
FF FF FF FF FF FF
As you can see, it's pointing to generic.asm
. We don't want it to point to that code, but our one instead. So what you need to do is rename generic.asm
(or whatever the name of your .ASM file is..):
FF FF FF FF FF FF
Save the .cfg to make our sprite code done. All we need to do now is insert this as a generator sprite (D0-DF) with Sprite Tool. Test it out right now on say.. the 4th screen on a level. Once you reach there, Mario should become caped if he's got atleast 10 coins. If not, nothing happens (This is assuming you've written my sample code above).
Open up the .ASM file again. Our current code is this:
Change the BCC Return
to BCC Return2
. You should be aware that this will cause an error. But what if, for example, your generator's code is about 100 lines, and you have no idea where exactly the error is? For that purpose, we'll intentionally write BCC Return2
and find out the proper way of handling errors.
Insert your generator with Sprite Tool again. You'll get the following error:
Error detecting in assembling .\generators\generator.asm...
The proper way to fix this error would be to open up a file generated by TRASM.exe
(The program that actually assembles your code) and look at the line number containing the error. This will help us to pinpoint the exact line where the error is and we can easily fix it that way.
Write "Yes" and abort assembling your sprite. Now open up your ROM's folder and look at the last created file. You'll notice the following files:
The .ASM file is actually your generator file. The only thing added is the "org $xxxxxxx" at the top of your file. This is not needed, so we'll just leave it.
The .LOG file (temp.log) is the actual error reporting file. You can open it up with a text editor like Notepad.
Notice the Pass 1, 2 and 3. Mainly, we refer to Pass 3
As you can see, Pass 3 says this:
"Error in 8.1/7: Symbol does not exist"
The bolded number, 7, is the line number
the error is on. What we need to do is go to that line number in our .ASM file and fix it.
Open up generator.asm
once again. Firstly, to allow us to jump to a line number, disable Word Wrap (Format -> Word Wrap).
Now press Ctrl + G. Write 7 as the line number and click OK. We'll get directed to this line:
Now notice the type of error noted:
"Symbol does not exist"
What this means is that we've got a symbol after a branching command which is okay, but what's wrong is that this symbol name doesn't even exist in the file. It should have been BCC Return instead.
This is how you'll debug your generator's code. It's also the same way you'll debug a sprite.
NOTE: TRASM is not case-sensitive. This means that you can use BCC return, and make the label Return. However, once we get to assembling with xkas, you MUST have case-sensitive label names. So it's best to stick to that in case you switch to assembling with xkas (we'll get to that later, as I said).
Another small note: You won't always get a new temp.log everytime you assemble your sprite and get an error. It deletes the old one and generates a new one each time.
That's it for generators. You can write ANY code you want in there. There is no limit how many opcodes you can use, or how many lines of code you write. As long as your code makes sense and there are no errors, your generator will work fine. Be sure to create a .cfg file for it too.
Lesson 2: Drawing A Simple 16x16 Static Sprite
Now you feel you're really efficient at coding generators, and it's time to move onto sprites. Coding sprites is a bit more difficult, but at the same time, it's not so complicated unless you start off by creating a "z0mg, 64x64 boss who jumps and throws hammers at Mario and has 10 HP!!!". That won't work. We'll start off with something simple - displaying graphics for a 16x16 static sprite. By the end of this lesson, we'll create a sprite that shows a blue Mushroom (but does nothing yet).
Likewise with generators, sprite's have an INIT and MAIN routine. We already covered that. But with regular sprites, we can actually write code in the INIT routine!
For this lesson though, we won't. In the next one, we might but for now, something simple.
Before actually starting, note one very important thing in mind:
When you write the INIT and MAIN routines of a sprite, the X register is ALWAYS used. This is for various sprite tables, like X speed, Y speed, direction etc. Because there are a maximum of 12 sprites being processed on screen, we use the X register to determine each one. The X register contains $15E9 - The current sprite being processed.
This means that if in any way you need to use the X register, you need to preserve it (PHX), and then pull it (PLX) after you've finished your routine that uses the X register. If you read my ASM tutorial, I stated that the X register must be preserved if you want to use it in a sprite for purposes other than the sprite tables itself. If you don't preserve and pull it and use it in your own code, you'll probably mess up the sprite index, which probably means your sprites won't show up on screen or something buggy will happen. This is very important to remember!
Now that we know some basic stuff, let's start off with the static sprite:
In the MAIN routine, we'll need a routine that will draw the graphics. For simplicity, we can just JSR
to another routine that will draw the graphics.
is an example of what I mean.
Now we actually need to draw our tiles. The first thing to do though, is place a routine that will get the sprite index ($15E9) to the OAM
. What's the OAM? It's just the big place where sprite tiles are written to.
For this, we'll need to jump to another routine, commonly known as GET_DRAW_INFO
right after we we call the graphics routine. You should have something like this
All I added was a jump to the GET_DRAW_INFO
routine in our sprite. We don't have that though, so what we'll need to do is add that into our sprite's code. You can find this routine is almost every sprite, the library file in the tutorial folder in Sprite Tool, or here
if you're lazy. Just copy that entire code (yes, all of it) into your .ASM file, at the very bottom of course.
Your sprite file should now look like this
now. It's a huge code for GET_DRAW_INFO
. We don't need to add the label/symbol for it because the routine that we pasted already has it.
Now that we've got GET_DRAW_INFO into our sprite, we're now ready to draw tile data for it. But first though, to avoid confusion, it's always a good idea to create a "header" for the graphics routine. By this, I mean neatly indenting it like this:
; GRAPHICS ROUTINE HERE
You'll notice that many sprites have this. It's there to easily find the graphics routine when you want to edit it. Note that this won't be reported as an error because it's treated as a comment (because of the semi-colons). Keep that 'header' right above your graphics label. You should have something like this:
; GRAPHICS ROUTINE HERE
Before we actually start writing tiles and data for the graphics tile, make sure you've JSR'd to GET_DRAW_INFO
There are 4 stages in writing the GFX:
- The X position
- The Y position
- The tile to use
- The "properties" of the tile
The first thing we do is write the X and Y position (horizontal and vertical) of the sprite.
The X And Y Position
After the call (JSR) to GET)_DRAW_INFO, the Y register will contain the index to the OAM. In case you don't know what that means, just assume that this sprite is the one being processed in terms of the GFX routine.
If you still don't get that, then it doesn't matter
- The X position of the sprite is in $00.
- The Y position of the sprite is in $01.
This means that we'll have the sprite's X and Y position in $00 and $01. However, this won't actually put it on the screen; to draw the sprite's X and Y position, we write to the sprite OAM. As I said before, the OAM is the area for sprite tile data.
The OAM starts at $0300
The X position of the sprite OAM is at $0300 indexed by Y.
The Y position of the sprite OAM is at $0301 indexed by Y.
Remember - we've got our sprite's X and Y position in $00 and $01. We just need to store to $0300 and $0301 indexed by Y to draw it's tile positions.
The first part of the code get's the sprite X position to $00 after the call to GET_DRAW_INFO. All we need to do is store it to the sprite's OAM, which is $0300 indexed by Y.
The second part of the code get's the sprite Y position to $01 after the call to GET_DRAW_INFO. All we need to do is store it to the sprite's OAM, which is $0301 indexed by Y.
tl;dr All you need to do to draw the X and Y position of the sprite (for a 16x16 sprite) is add that 4 line code at the beginning of your graphics routine..
We've now stored the X and Y position of the sprite. Now we've got this to do:
- Writing the X position of it.
- Writing the Y position of it.- Writing the graphics tile to use for it.
- Writing the "properties" of the sprite. This includes the palette, X and Y flip, priority, which graphics page to load from etc..
Now it's time to draw the graphics tile, which is relatively easy.
The Tile To Store
The tile you want your sprite to display must be stored to $0302,y. Like if I drew a mushroom tile, I'll store the value #$24 to $0302,y.
But how do we know which tile it is and all that? It's quite simple.
Open up the 8x8 editor in Lunar Magic. Go to the graphics area, i.e., the area with GFX00 and GFX01.bin.
You can draw any 16x16 tile from there. However, the tile value is determined like this:
It should be straightforward and easy, but here are the steps for it:
- Open the 8x8 editor.
- Of any 16x16 tile you want to select, select the "upper-left" 8x8 tile of it, as shown in the diagram above (messy one I know, who does that really matter?).
- Note the tile number Lunar Magic says it as. In this case, the value is 0x324
- The last 2 digits is what we pay attention to. Ignore the 0x3 (NOTE: It is 0x2 in LM 1.65 or lower but 0x3 in LM 1.7x) but pay attention to the last 2 values it shows - that's your tile number.
So after a short (long?) process, we've got our tile number - 24. You can choose any tile you want:
- The appropriate tile for the fire flower would be 26. (0x326)
- The appropriate tile for the key would be EC. (0x3EC)
- The appropriate tile for the POW would be 42. (0x342)
Move onto the next page in the 8x8 editor - SP3 and SP4. You'll notice that the tiles start fresh all over again, meaning that you'll see another tile 00 there.
This does also mean that there's another tile 24 - tile 0x324
! However, there's an option in the .cfg editor which determines the page number to use and it's called "second graphics page". But as we haven't reached the .cfg part yet, we'll do that later. Alternatively, you can choose the page number to use in the property byte
of the sprite, which we'll learn next.
Anyway, you store the tile number to $0302,y. Since we're going to draw a blue Mushroom, I'm going to store #$24 to $0302,y:
It's as simple as that.
The Property Byte
Now we're going to write the property byte, which is at $0303,y. The property byte handles the Y-flip, the X-flip, the priority of the tile, the palette to use, and the page number to use. It's basically the data which controls the sprite's appearance.
Because it's handles all those things, it's called:
Kind of long and confusing, I know. But soon you'll remember it, like me
The YXPPCCCT format is handled in bits
. That's why there's 8 bits - each bit represents each "property". Also note that they can only be 0 or 1 because they're bits of course.
Y -> This controls the Y-flip of the sprite. If 1, it Y-flips.
X -> This controls the X-flip of the sprite. If 1, if X-flips.
PP -> This controls the priority of the sprite.
00 = Lowest priority, and appears behind objects and sprites.
11 = Highest priority, and appears in front of all objects and sprites.
You can use any binary value from 00-11:
00 - Lowest
01 - Low
10 - High
11 - Highest.
Normally though, I don't use priority in the sprite, so it's left as 00.
CCC = The palette number to use, in binary. It works from 000-111 in binary, once again:
000 = Palette 8
001 = Palette 9
010 = Palette A
011 = Palette B
100 = Palette C
101 = Palette D
110 = Palette E
111 = Palette F
So if you wanted your sprite to use palette F, you'd need to use 111
as the CCC portion of the byte.
Likewise, if you wanted to make your sprite use palette A, you can set the CCC bit to 010
. Keep a list of the CCC bits and their palettes if you're confused.
T - Graphics Page. If 0, it loads from SP1/2. If 1, it loads from SP3/4.
I already stated that when you start SP3 and 4, there are new values for 00-FF for the tiles. If you want these values to be loaded, you need to make this "T" bit 1. If you want your tile to be loaded from SP1/2, you set this bit to 0. Guess which one we'd need if I wanted to draw a blue Mushroom?
So how does this actually work? Simple. You set all of the bits to your liking, and convert the value into hex and store it to $0303,y.
;Y -> Y flip
;X -> X flip
;PP -> Priority
;CCC -> Palette
;T -> Graphics page
So this will make the sprite:
- Have Y flip (the bit is set)
- No X flip (the X bit is clear)
- No priority (PP = 00)
- Uses palette 8 (the CCC part is 000)
- Uses the first graphics page (because T = 0)
It's quite simple to comprehend. If you're confused, in comments, write down YXPPCCCT
and below it, write your property data.
As I said before, I'm going to create a blue Mushroom sprite (well, only the graphics..).
For that, I'll need:
- No Y Flip.
- No X Flip.
- No priority (this is optional, though).
- Uses palette B (Because it's blue in that palette).
- First graphics page.
As you can see, all I needed to do was set the CCC part to 011 (the value for palette B is 011). Nothing else needs to be set.
So I've got the binary value 00000110, or #$06
in hex (I used the Windows Calculator for Binary -> Hex). You need to store it to $0303,y - the property byte. You can store it in binary, or in hex. It doesn't matter which one you use.
Before storing the property byte to $0303,y, it is also necessary to add in the sprite priorites byte ($64) as well. This can be done using ORA $64
It's a bit easier to comprehend the values to binary, so I'd stick to that one. You can use hex as I said, though.
Additionally, it is possible that you can configure the palette and graphics page through the .cfg file. Instead of including the CCCT
portion, you load $15F6,x, a sprite table:
PHX ; This is only necessary if you are using X for something else in the graphics routine.
; This is only necessary if you are using X for something else in the graphics routine.
So we've written the X and Y position, the graphics tile, the properties byte of the sprite. There's nothing missing, right?
Nothing left in drawing the sprite anymore. There's just a bit of the graphics routine left - mainly to do with the call to the routine that writes our graphics to the OAM. No we need to:
- Increase the OAM size 4 more times
- Load Y with the tile size - for a 16x16 tile, we load with 02. For an 8x8 tile, we load it with 00.
- Load A with the number of tiles are drawn - 1 (because 00 counts). We only drew one 16x16 tile, so we'd load it with 00.
- Call the routine that writes our graphics to the OAM.
One 8x8 tile uses 4 OAM bytes, and to draw the next 8x8, you need to use 8 OAM bytes. We use INY to get to the next OAM slot. There are 4 tiles, so we increase Y four times.
You'd need to increase Y (the OAM) 4 times to make it draw all 8x8 tiles of our 16x16 sprite. It's always
important you increase Y four times after writing your tile data unless you are drawing just one 8x8 tile. In that case, INY isn't needed.
Now we just need to load A with the number of tiles drawn -1 and Y with the size of the tile and then call the routine at $01B7B3
through a JSL. So the ending is like:
- Load Y with our tile size (00 = 8x8, 02 = 16x16).
- Load A with the number of tiles drawn -1.
- JSL to $01B7B3.
For the first part, our tile size is 16x16. If it's 16x16, we write an 02
in Y: LDY #$02
Secondly, we'd load Y with 00
instead of 02.
- 02 means the tile size is 16x16.
- 00 means the tile size is 8x8 (?).
You shouldn't use any value other than these 2.
We load A with the number of (16x16) tiles drawn - 1 (this is because 00 also counts).
The Mushroom tile I drew was only one 16x16 tile, right? That means I'd put 1-1=0 into A:
LDY #$02 ; This means the tile drawn is 16x16.
LDA #$00 ; The number of tiles I wrote - 1.
Finally, we call the OAM routine at $01B7B3
to write our tiles:
..And our graphics routine is finally over! However, don't forget the RTS at the very end.... remember that we JSR'd to this graphics routine? I'd put an RTS in the end to make the code end there.
So we've finally created a simple 16x16 graphics routine. By now, your grapics routine should be like this.
That's all we need to do to draw a simple 16x16 tile graphics routine! Congratulations. Once we make the .CFG file (in the next lesson) which is MUCH easier than the .ASM file, we'll be able to test our graphics routine.
NOTE: Drawing a 32x32 graphics routine is slightly harder. We'll learn about that in a later lesson.
Lesson 3: The .CFG File For Sprites
We've fully written our ASM code - the one to draw a blue Mushroom. We can't insert it yet because we don't have a .cfg file yet. Like a generator, we'll copy an existing .cfg file from any one in the sprites
folder of Sprite Tool, rename it, and repoint the .ASM file it points to. You can do the last one by opening the .CFG file and changing the .ASM file written in it, like we did with the generator.
Unlike generators though, we'll edit some .CFG options. Included with Sprite Tool is a .cfg editor
for sprites. I am sure you are aware of it, how it looks like and what it does. We'll be using the .cfg editor from Romi's Sprite Tool though, because it covers what are "Unknown Bits" in mikeyk's (?) cfg editor. It can be found here
Open up your .cfg file with the .cfg editor. The window will be like this:
We'll be going through most of the options here.
Sprite Tool Info
- Tweak/Acts Like
This option, as clearly stated in the .cfg editor, is for Sprite Tool and not the sprite. For all custom sprites, we either put Tweak or Acts Like. If you select Tweak, then it means we're just modding an original SMW sprite. If this option is selected, we can't point to our ASM file in the .cfg editor. The Acts Like setting chooses how our sprite behaves like.
Since our sprite is not a tweak of an original one, we'll be using Acts Like
. However, we don't want our sprite to act like a Goomba, of course. We want it to act like a null
sprite, which does nothing. This is because for a null or unused sprite, we can easily choose our options without having to worry about how the sprite actually behaves. A null sprite that's generally used in sprites is 36
, an unused sprite in SMW. I normally see that in many custom sprites.
After that, there's the ASM File
option. That should be obvious - it calls that particular .ASM file.
RAM Address 1656
Sprites use some sprite tables for normally used options, like "can be jumped on", "dies in a puff of smoke" etc. RAM Address $1656
controls a few of these. First, there's the object clipping
of the sprite.
The object clipping
is how the sprite interacts with objects. For several sprites, the object interaction value is set to 00, because they interact best with objects at that value. You can only use object clipping values from 0-F.
The rest again are self-explanatory. Seriously.
RAM Address 1662
This controls the sprite clipping - which is actually the hit box of the sprite, meaning it's interaction field can be 16x16, 16x32, 32x32 etc depending on what you write here. There are plenty of sprite clipping values to use - 00-3F. However, how do you what values are for what sprite size? This
document will be really helpful for that. 00 is a good size for 16x16, so we will use that.
This is one simple option that if checked, makes the sprite assemble with xkas. If it doesn't, it'll assemble with TRASM.exe. Since we're coding sprites for TRASM.exe right now, we'll leave this unchecked.
The Extra Info can be used for some custom purposes when coding a sprite. For example, it can be used as a timer. Let's say that our sprite explodes after XX frames defined in Extra Property 1. The user can easily configure the time it takes to explode by then writing the # of frames in the Extra Property 1.
Along with Extra Property 1, there's Extra Property 2. This again, can be used for additional purposes. For example, the user can set to 01 to make our sprite stay on ledges, and 00 if it doesn't stay on ledges.
We won't be using the Extra Property bytes for now, so we'll come to them again later.
RAM Address 166E
This is basically the sprite table address that controls the graphics and palettes. Firstly, you have the first or graphics page option. If selected, the sprite will drawing graphics from SP3 and SP4.
Again, the rest are similar. You choose the palette from the drop-down box and the graphics page to use. We already set the palette in the .ASM file ($0303,y, the sprite properties if you recall), so just set the graphics page unchecked so the Mushroom is drawn from SP1 and not SP3!
RAM Address 167A, 1686 And 190F
... are all self-explanatory. They're customisable options for your sprite. Tick any of the options you want and uncheck the ones you don't want. An important one you usually uncheck is the "Don't Interact with Objects" option. If it's set, the sprite won't interact with any objects and will directly fall down the screen. Unless you want your sprite to not interact with objects, like a Big Boo sprite, you should always turn this option on.
How Do These Addresses Work?
You might be wondering how a .cfg editor can edit some sprite settings. In the 12 byte Sprite Tables starting from $1656, $1662, $166E, $1662, $167A, $1686 and $190F
, certain bits are used for a sprite's behaviour. For example, the "can't be kicked by a shell" is a bit 4 in the sprite table $167A. If it's checked, that bit will be set.
The same goes for all other bits. The box where you can enter a value beside the RAM Addresses is the final value that address gets set to. For example, if I only check the "can't be kicked by a shell" option in $167A, $167A will become #$10 in the sprite's code.
For each bit 01234567, the first one is 0, and the last one is 7. 8 bits are only used in addresses from $167A, 1686 and 190F. The rest use 4 bits or so.
Assembling Our Sprite With The CFG Editor
We've talked about the .cfg editor now, and it's time we work on our blue Mushroom sprite again. The only thing relevant now is the sprite clipping value. Since it's 16x16, a good sprite clipping for that size is 00
(the document I linked earlier helps a lot here). So stick that to 00 if it isn't already. Also ensure that the "Dont Interact with Objects" is unchecked, otherwise the sprite will fall down the screen!
Maybe sure the "Don't use default interaction with Mario"
is checked - this allows it to have separate interaction (instead of SMW's default interaction, which is allowing you to stomp on sprites, hurt when touched from sides etc.)
Our .cfg settings are done now. We've just got to insert the sprite will Sprite Tool. Insert the .cfg file as a regular sprite number (00-CF) in the sprites.txt list and assemble it. If for some reason, your sprite reports an error (which it shouldn't..), refer to temp.log. It should definitely insert successfully though. If it did, congratulations. It's time to test our blue Mushroom sprite.
Place the sprite anywhere in Lunar Magic on the ground and set the Extra Info
in Lunar Magic to 02. Our sprite is very simple and doesn't use the extra info yet, so that's why we'll leave it as 02. Test your sprite out now. You should be seeing a blue Mushroom which doesn't do anything yet! Isn't it awesome that we've created some graphics for a sprite?
Lesson 4: Giving The Sprite Interaction
At the end of this lesson, our blue Mushroom sprite will actually be made to do something - give Mario 10 ten coins along with playing a sound effect when touched and then erase itself.
To do this, we've got to add the following in our sprite's main routine:
- Check if Mario is touching the sprite.
- If not, return.
- Otherwise, add 10 to $13CC (better option than $0DBF) and play a sound.
- Erase sprite.
To check if Mario is touching a sprite, we can call a routine (JSL to one) that checks for Mario/Sprite contact. This routine is $01A7DC
After we call this routine, we can check if there is contact if the carry flag
is set. What does this mean? Simple:
;Contact has been made if BCC doesn't branch.
Right after we called the graphics routine for the sprite (JSR Graphics
), we'll add the code for contact check:
Add those lines in your sprite. In between the BCC NoContact
label, the code for "Mario touched the sprite" will be written.
We'll add some basic code for the sprite itself. When Mario touches it, we'll add 10 coins and play a sound effect:
STA $13CC ; Add 10 coins flag
LDA #$01 ; Some sound effect
You can add whatever code you want. Normally for enemies, you check if Mario is touching the sprite from above (i.e., if he jumped on him). If he has, it kills the sprite. Otherwise, it calls the hurt routine ($00F5B7).
This is just a simple sprite though, so it just does this. The last thing to add now is that the sprite should erase itself. How do we do this? Using a sprite status table.
is the sprite status table. However, remember that I said we use the sprite index (X register) for all sprite tables? That's why we'll be using $14C8,x
These are the values for $14C8,x:
02=Killed, falling off screen
05=Sinking in lava
06=Turn into coin at lvl end
07=Stay in Yoshi's mouth
0C=Powerup from being carried past goaltape.
Quite a lot. When $14C8,x is 02, it'll fall off the screen. What we wanted our blue Mushroom to do is erase itself. If you can see, 00 makes it non-existant, i.e., erased/removed. We can just set $14C8,x to 00 to erase the sprite.
STA $14C8,x, or:
Both of these will work, but I'll use the second one (STZ $14C8,x) because it's obviously faster and efficient.
By now, you're sprite's code should look like this
Test the sprite out by inserting the .cfg file through Sprite Tool. If successful, congratulations. You've made your first working sprite!
So what we did in this lesson was:
- Added a code to check for Mario/Sprite contact - JSL $01A7DC.
- If there wasn't any contact (BCC NoContact after the JSL), return.
- Otherwise, do "Mario touches sprite" code.
- Erase the sprite. This is done by setting $14C8,x, the sprite status table to 00 so the sprite becomes non-existant.
Well, now that we've made a working sprite, we're going to move onwards to make an animated sprite. All an animated sprite is, is a small alteration of the graphics routine.
Lesson 5: Adding Some Common And Important Routines
Open up your sprite's .asm file, the blue Mushroom one.
We're going to add some common behaviour to all standard sprites. Firstly, for easiness, we'll separate the sprite's code from the main routine, like this:
;.. the rest of the code here.
This is usually a good idea. The sprite still has the same functionality as before, but we've packaged the sprite's code in it's our routine. It might be a bit unclear to why we've done this, but it really helps to clean up the code a bit and simply things later.
Note that now we've JSR'd to our sprite code. This means that in the sprite's function, we'll replace will RTL
with an RTS
, because we called it with a JSR. So change the RTL below the NoContact
label to an RTS. From no onwards, when we need to return, we'll use RTS instead of RTL - because we called the sprite's code through a JSR.
Next, in many sprites, it's a good practice to change the data bank to the one you're reading from. This means we'll be adding this RIGHT after dcb "MAIN":
The JSR SpriteCode should already be there, from above it we pushed the data bank and loaded from our new one and below the JSR SpriteCode
, we pulled back the old data bank.
The reason we change the data bank is because otherwise setting some sprite tables, such as the X speed values, wouldn't work. Now our data will be loaded from the ROM from the same bank that our code is running.
From now on, we won't be touching the main routine again, we'll focus on the sprite's code instead. By now, your sprite should look like this
. What I've done was separated the sprite's function from the main function, and changed the data bank when the sprite's subroutine is run.
Now that our main routine
and our sprite routine
has been separated, our sprite code will be much easier to work with.
After we call our graphics routine, we add some behaviour that pretty much all sprites have:
LDA $14C8,x ;\
CMP #$08 ; | If the sprite is dead or not it it's normal state, return.
BNE RETURN ;/
LDA $9D ;\
BNE RETURN ;/ If sprites are locked, return.
... our code here.
Right after the call to the graphics routine and before our JSL $01A7DC (or whatever you had after the graphics routine), we add this basic behaviour to our sprite.
The first makes sure that the sprite is alive and functioning before it continues with our code. This prevents the sprite from doing it's function if it's killed by a spin jump or a star etc. Remember how I said that if you set $14C8,x to 02, your sprite falls down the screen? If it did that, we terminate the entire code sprite because the sprite status is no longer 8, but 02. It's always 8 when the sprite carries out its functions.
The second check makes sure that the sprite doesn't do anything when sprites are locked or frozen. You know like when you collect a Mushroom or get hit, sprites are temporarily frozen right? We make our sprite do that as well, it won't do anything if you get a powerup or anything that freezes sprites temporarily.
Our last behaviour to add is calling a subroutine that handles the sprite if it's offscreen. Once the sprite is offscreen, it won't process it. We need a routine that does that - SUB_OFF_SCREEN_X0
SUB_OFF_SCREEN_X0 again, like GET_DRAW_INFO, can be found in several sprite codes, the library.asm included in Sprite Tool's tutorials folder, or here
if you're lazy again. Add the whole routine at the bottom of your file, below GET_DRAW_INFO's code.
By now, your sprite's .ASM file should look like this
I've added some basic behaviour, but I need to call SUB_OFF_SCREEN_X0
because I only added that routine in my sprite, but didn't call it. To call it, just add a JSR SUB_OFF_SCREEN_X0
RIGHT before the JSL $01A7DC and RIGHT after the BNE RETURN after the LDA $9D:
LDA $14C8,x ;\
CMP #$08 ; | If the sprite is dead or not it it's normal state, return.
BNE RETURN ;/
LDA $9D ;\
BNE RETURN ;/ If sprites are locked, return.
... our code here.
As you can see, SUB_OFF_SCREEN_X0 is called through a JSR because the routine ends with an RTS.
The basic behaviour for the sprite is now done. Re-insert your sprite's code with Sprite Tool and it should insert successfully. We've now made our blue Mushroom sprite, but there's one thing we could add to make it slightly better.
If you place a walking sprite near it, you'll notice that it will go through the blue Mushroom. To make it a bit better, we'll make the Mushroom interact with other sprites as well.
is a routine you can call with a JSL to make the sprite interact with other ones as well. You can call it directly before the interaction with Mario routine ($01A7DC). To make sure this works, make sure that the "don't interact with other sprites"
is unchecked (in the cfg).
By now, your final code should be this
. Test it out with Sprite Tool. When a sprite comes in contact with the blue Mushroom, it should turn around away from it.
Congratulations. We've made our first functioning sprite. Let's head on towards making an animated sprite.
Lesson 6: Creating An Animated Sprite
In our last lesson, we made a pretty good Mushroom sprite. Now we're going to make a sprite that animates between 2 frames - for this, we'll make a sprite with Bob-Omb graphics. A bob-omb, as you already know, switches between it's walking frame and moving frame.
I don't want you to go through the trouble of setting up the basic template for a sprite, so you could use this
sprite to start this lesson. Insert it with Sprite Tool - you'll notice that it's the still/steady frame of a Bob-Omb. All I did was change the property byte ($0303,y), and the tile to use ($CA, which is the Bob-Omb still tile).
The walking frame of the Bob-Omb is tile CC
, if you look carefully at the 8x8 editor in GFX02.bin. The still frame of the Bob-Omb is tile CA
, which is what is in the graphics routine now. Instead of the sprite to only load tile CA, we want it to switch between tile CA and CC.
A good way to do this would be to use the frame counter - $13. If you don't know about it already, it increments by itself every frame. So depending on $13, we'll load different tiles to the sprite tile data, $0302,y.
The first thing I'll do is make a table with the frames I'll be using:
TILEMAP: dcb $CA,$CC
is the name of the table I'm using, and I've added 2 values to it - CA and CC. These are the frames I'll be using for the sprite. You can put this table at the beginning of the Graphics routine, before the Graphics label or at the end of it. I normally put it before the Graphics label though, as it makes it easier to work with.
Now we need to alter the code that writes the tile data. We currently have:
Firstly, we're going to need to index our tiles with the X or Y register, of course. But:
- The X register is being used for the sprite tables.
- The Y register is being used for the sprite's OAM index.
We are not using the X register for the graphics code, are we? Nope, so we call preserve it (PHX) right before we write our tiles, and pull it back (PLX} after we've done writing our tiles.
Now, we should have this:
LDA #$CA ;\ We haven't done this part yet.
STA $0302,y ;/
In the place of the LDA #$CA STA $0302,y, we're going to put the code that animates our sprite. We'll use $13 for this.
Firstly, we'll get bit 0 as an index:
STA $0301,y ; The Y position code, which is above our one.
What we're doing here is getting bit 0 of $13, and putting it into X (because it's free since we preserved it). Since $13 increments every frame, it is either 0 or 1.
Now what we're doing to do here is depending on if it's 0 or 1, we'll either load tile CA or tile CC. Since we've got the bit indexed in X, we can use that:
STA $0301,y ; The Y position code, which is above our one.
When bit 7 of $13 is 0, it'll load the first value from the table, which happens to be CA - the still frame.
When bit 7 of $13 is 1, it'll load the second value from the table, which happens to be CC - the walking frame.
Since $13 increments all the time, it will either be 0 or 1, so it will "switch" between these 2 tiles.
Lastly, we're going to pull back the X register, which contains the sprite index. We don't want to make the stack crash!
So our final code for the tile routine would be:
Replace that with this:
LDA #$CA ;\
STA $0302,y ;/ Old code.
Ensure that you've got the TILEMAP table as well, otherwise you'll get an error. As I said, it's best if it's right above the Graphics
Re-insert your sprite with Sprite Tool. What do you notice?
It animates between the two frames, but at an extremely fast rate!
We'll need to slower down the animation rate a little. But how do we do that?
We add LSR's
RIGHT after loading $13. Like this:
I only added one LSR there. The more you add, the slower the animation will be. If I add 4 LSRs, the animation will be slow.
You don't need to know why LSRs slow the animation rate, unless you want to confuse yourself.
Test the sprite out with an LSR added after loading $13. Keep adding LSR's (until you've got about 4) and notice the difference in the speed of the animation. With about 3 LSR's, our Bob-Omb sprite has a normal walking speed. So our final code for loading tiles would be:
LSR ; | Slowers the animation rate.
We can, of course, add more frames to our sprite. But it has to be a power of 2 -1
(because that's just how AND works).
So you can put AND #$03, #$07, #$0F, #$1F, #$3F, #$7F or #$FF (but that's useless). But remember though, depending on how many values you use with AND, you'll need to have that many values in your table +1. AND #$03, for example, requires me to make a 4-byte table.
If you want your sprite to animate between 4 frames, do this:
LSR ; | Slowers the animation rate.
At the beginning of the Graphics routine, before the Graphics label:
TILEMAP: dcb FRAME1,FRAME2,FRAME3,FRAME4
Anyway, we've made our Bob-Omb sprite animate between 2 frames - the walking one and the still one. If it works successfully, we've made an animated sprite.
Lesson 7 : Moving The Sprite
I am sure you want your sprite to move atleast now. It' be nice to see it animate and move!
To store to Mario's X speed, you either store a postive
(01-7F) or negative
(FF-80) depending on the direction. For storing to a sprite's X speed, it's pretty much the same. The general idea to set the X speed to $B6,x
and the Y speed (if needed) to $AA,x
. Because they're also sprite tables, they're indexed with X as well.
Depending on the direction of the sprite, we'll store the right and the left speed. The direction of the sprite is often stored at $157C,x:
00 - Right
01 - Left
First, we'll make a two-byte table with the speeds. Place it above the RETURN
XSPEED: dcb $08,$F8
; Speeds, right and left.
I've named my speed table XSPEED
and set the sprite's speed to 08 and F8, which is a normal walking speed.
In the sprite's code, we'll need to store to $B6,x
indexed by the direction. We can put the code after JSR SUB_OFF_SCREEN_X0
. The code we'll be using is:
LDY $157C,x ; Load the direction in Y.
LDA XSPEED,y ; Speed based on direction.
STA $B6,x ; Store to sprite's X speed.
Sprites that walk on the ground generally have a Y speed (gravity) of #$10.
We've now stored the speed for the sprite. However, unlike storing speed to Mario, we need to call a routine that applies the code that stores to the speed of the sprite. This means that after writing to the X or Y Speed ($B6,x and $AA,x respectively), we need to JSL to another routine to update the sprite's position.
This routine is at $01802A
. It can be accessed by a JSL.
XSPEED: dcb $08,$F8
Here, we've set the X speed based on the direction to 08 and F8 by a table. NOTE: The table should be placed in such an area so that it doesn't execute as codes. It's best to place is before or after an RTS in a subroutine.
Then we set the Y speed to #$10, and called the routine that applies our speed to update the sprite's position.
So giving our sprite speed is now done. If you'd like, you can modify the speed table. Test your sprite now.
There are still a few more modifications to make. Firstly, you'll notice that the sprite initially does not face Mario, but that's not what we're going to fix now. Firstly, if you place a wall/object, our sprite will walk behind it. We don't want it to do that - instead, we'll make it flip it's direction ($157C,x) if it comes in contact with a wall.
How do you check if a sprite is touching a wall? Simple. Like with Mario's direction status, $77:
xxxxUDLR (Up, Down, Left, Right)
Those correspond with the bit 8 bits in $77. Normally, to check if Mario is touching a wall, you would check if the LEFT and RIGHT bit are both 1. That means you'll use AND #$03
to check for the first and second bit (01 and 02 respectively).
;Is touching a wall.
You do the same with a sprite, except that the sprite uses a direction table at $1588,x
. It's used exactly in the same way as $77:
;Is touching a wall.
When it's touching a wall, we'll flip the direction using EOR.
We'll put that right at the beginning of our sprite code, before updating the speed. Our final code is will be like this
Test your sprite now. When it touches a wall, it'll flip its direction. But you'll also notice something else - it's facing or looking towards one direction all the time.
We don't want this - so we'll need to X flip the sprite when it's going right
. If it's going left, we'll clear out the X flip. You already know that in the YXPPCCCT
format of the sprite properties, the "X" bit is for the X flip.
To set that bit, we can add #$40
more to our property byte. We'll do this when going right.
Currently, in our property data of our graphics routine we have:
If we add 40 more, i.e. make it 47, the graphics will be X flipped so it looks like the sprite's facing the other way. We'll need to index this by direction and either load 47 or 07 (47 for right, 07 for left) into $0303,y.
There are 2 ways to do this. Firstly note that you cannot index with X because LDX $157C,x
is NOT a valid addressing mode. We can either:
- Index with Y, but first preserve the Y index. We preserve it because it's being used for the sprite's index to OAM!
- Store $157C,x to empty RAM, and index with that into X instead.
For the first method:
PROPERTIES: dcb $47,$07
First we pushed the Y register because it still has the sprite's OAM value. Then I load a table (NOTE: I've named my table PROPERTIES) which either has the value of 47 or 07 - it has 47 when the sprite faces right to X-flip the graphics and 07 when going left because the graphics won't be X flipped then. Before storing, we MUST
pull the Y register back to recover the OAM index so it stores properly. If it didn't, we'd be storing to $0303 + the direction value (which is in Y), which is NOT what we want.
The second method involves storing $157C,x to empty RAM RIGHT after the call to GET_DRAW_INFO in the graphics routine. It's very common to store the direction to $02
Then we can use $02 as an index in X and make a table and store to $0303,y the same way we did with our first method. Firstly, RIGHT after the call to GET_DRAW_INFO, add this:
;..rest of the code here.
Then once we've reached the property byte part of the graphics routine, we can index with X. Note that we'll still have to preserve it, because we'restill
using the X register for a purpose other than sprite tables - indexing with $02 isn't making a sprite table, is it?
As you can see, this also works. "What's the point of using this over Y though when is still uses the same number of bytes?" is what you might as now. Well, generally this method is a good idea. And we can save 2 bytes because...
We've pushed and pulled X 2 times, even though we're not using a sprite table in between these 2 codes! We can just cut-off the second PLX and PHX, and place the PLX at the end of this code:
This makes our code more efficient, and it's prefered that you stick to the second method - using $02 - to X-flip your sprite.
Add the sprite/sprite interaction routine to the sprite as well - if you forgot, you JSL to $018032. Add that right before our sprite code ends. We're not going to add Mario/sprite interaction yet.
Our code should be now like this
. You can see the slight modification in the graphics routine.
Test our the sprite. We've added some generic behaviour to it and now it'll animate, move and interact with other sprites. We're going to make 2 more little additions, and then we're done with this lesson.
tells us the sprite's direction status or status with an object. Bit 2 (#$04) will be set when it's on the ground. We are NOT going to store speed if it is in air - doing this will mess up the sprite's "gravity".
So we're going to make a slight additional to our sprite's routine before writing the X and Y speed. You should have this code:
Before loading Y with $157C,x, we'll check if the sprite is not
on ground. The bit is clear if it's in air, so if the bit is clear, we'll bypass the speed code:
We will still update the sprite's position if it's in air (JSL $01802A).
Now your sprite's code should look like this
Test it out. Our sprite animates and moves like a real Bob-Omb sprite. Now there's just one more thing to do..
The sprite need to init
ially face Mario. Place the sprite on both sides of Mario - left and right - and you'll notice that on one side, it will face the wrong way or face the direction Mario is not in.
We're going to make it face Mario when it first comes on screen. Note the part in bold - INIT. Since we're going to make it face Mario initially, we'll add a code in the INIT
routine to make it face Mario's direction.
Another routine will help us here, it's called SUB_HORZ_POS
. What this routine does is get's the sprite's direction relative to Mario's in the Y register. This means that if Mario's facing left, the sprite's direction (in the Y register) will also be left. If Mario faces right, the sprite's direction will also be right.
This routine, once again, can be found in many sprites, the library.asm file from Sprite Tool's tutorial folder or here
if you're still lazy.
It's a relatively small routine. Copy it to the bottom of your sprite's .ASM file like with GET_DRAW_INFO
. The routine ends with an RTS, so we'll JSR to it. Since the Y register contains the sprite's direction relative to Mario's, we can transfer it to the accumulator and store to the direction table $157C,x (We can't directly do STY $157C,x because that doesn't exist).
..Should be our INIT routine now. I don't want to link to another update of our sprite, because all we did was add 3 lines of code in the INIT routine.
Anyway, test your sprite now. Place the sprite on both the left and right side of Mario and you'll notice that it initially will walk towards his direction.
Congratulations. We're done with our second simple sprite and our first animated and moving sprite. Read Part 2 of this tutorial to learn how to make more advanced sprites.