Banner
Views: 318,894,306
Time: 2014-10-31 12:54:52 AM
7 users online: o Ghostbuster Beetle, Lukitu88, LX5, Murderous Mindevous, Dolan Duckula, o Slash Man, Wario man21 - Guests: 24 - Bots: 36Users: 25,775 (1,099 active)
Latest: GlitchWorld
Tip: If you place Dragon Coins in a level, make sure there are at least 5.
Sprite Programming
Forum Index - SMW Hacking - General SMW Hacking Help - Tutorials - Sprite Programming
Pages: « 1 2 3 4 5 6 ... 8 9 »

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 :P

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:

dcb "INIT"
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:

dcb "MAIN"
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:

dcb "INIT"
RTL

dcb "MAIN"
RTL


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:


dcb "INIT"
RTL

dcb "MAIN"
STZ $0DBF
RTL


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:

dcb "INIT"
RTL

dcb "MAIN"
LDA $0DBF
CMP #10
BCC Return
LDA #$02
STA $19
Return:
RTL


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:

generator.asm <- This is the file containing your generator's main code.
generator.cfg <- 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:

03
FF
FF FF FF FF FF FF
FF FF
generic.asm


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 to generator.asm (or whatever the name of your .ASM file is..):

03
FF
FF FF FF FF FF FF
FF FF
generator.asm


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).

Error Handling


Open up the .ASM file again. Our current code is this:


dcb "INIT"
RTL

dcb "MAIN"
LDA $0DBF
CMP #10
BCC Return
LDA #$02
STA $19
Return:
RTL


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:

tmpasm.asm
temp.log


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 for errors.

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:

BCC Return2

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:


dcb "INIT"
RTL

dcb "MAIN"
RTL


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.

Here is an example of what I mean.

Simple right?

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 now.

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
;===================================

Graphics:

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 :P

After GET_DRAW_INFO:

- 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.

LDA $00
STA $0300,y

LDA $01
STA $0301,y


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:

LDA #$24
STA $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:

YXPPCCCT

Kind of long and confusing, I know. But soon you'll remember it, like me :D

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.

Lastly:

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.

Example:
;Y -> Y flip
;X -> X flip
;PP -> Priority
;CCC -> Palette
;T -> Graphics page

;YXPPCCCT
;10000000

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.

;YXPPCCCT
;00000110

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.

LDA #%00000110
ORA $64
STA $0303,y


Or:

LDA #$06
ORA $64
STA $0303,y


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.

LDA #$06
ORA $64
STA $0303,y


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.
LDX $15E9
LDA $15F6,x

PLX ; This is only necessary if you are using X for something else in the graphics routine.
ORA $64
STA $0303,y

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.

INY
INY
INY
INY


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.

With Y:
- 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:

JSL $01B7B3

..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.

Option

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.

Extra Info

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:

JSL $01A7DC
BCC NoContact

;Contact has been made if BCC doesn't branch.

NoContact:
RTL


Right after we called the graphics routine for the sprite (JSR Graphics), we'll add the code for contact check:

JSL $01A7DC
BCC NoContact

NoContact:
RTL


Add those lines in your sprite. In between the BCC NoContact and 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:


LDA #10
STA $13CC ; Add 10 coins flag
LDA #$01 ; Some sound effect
STA $1DFC


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.

$14C8 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 instead.

These are the values for $14C8,x:

00=Non-existant
01=Initial
02=Killed, falling off screen
03=Smushed
04=Spinjumped
05=Sinking in lava
06=Turn into coin at lvl end
07=Stay in Yoshi's mouth
08=Normal routines
09=Stationary/Carryable
0A=Kicked
0B=Carried
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.


LDA #$00
STA $14C8,x, or:

STZ $14C8,x


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:


dcb "MAIN"
JSR SpriteCode
RTL

SpriteCode:
JSR Graphics
;.. 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":

PHB
PHK
PLB

JSR SpriteCode

PLB
RTL


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:

SpriteCode:
JSR Graphics
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.

JSL $01A7DC
... our code here.

RETURN:
RTS


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:

SpriteCode:
JSR Graphics
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.

JSR SUB_OFF_SCREEN_X0

JSL $01A7DC
... our code here.

RETURN:
RTS


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.

$018032 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


TILEMAP 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:

LDA #$CA
STA $0302,y


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:

PHX
LDA #$CA ;\ We haven't done this part yet.
STA $0302,y ;/
PLX


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:

LDA $01
STA $0301,y ; The Y position code, which is above our one.

PHX
LDA $13
AND #$01
TAX


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.

00000001 ; 1
00000000 ; 0

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:


LDA $01
STA $0301,y ; The Y position code, which is above our one.

PHX
LDA $13
AND #$01
TAX
LDA TILEMAP,x
STA $0302,y


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:

PHX
LDA $13
AND #$01
TAX
LDA TILEMAP,x
STA $0302,y
PLX


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 label.

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:


PHX
LDA $13
LSR
AND #$01
TAX
LDA TILEMAP,x
STA $0302,y
PLX


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:


PHX
LDA $13
LSR ;\
LSR ; | Slowers the animation rate.
LSR ;/
AND #$01
TAX
LDA TILEMAP,x
STA $0302,y
PLX


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:


PHX
LDA $13
LSR ;\
LSR ; | Slowers the animation rate.
LSR ;/
AND #$03
TAX
LDA TILEMAP,x
STA $0302,y
PLX


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 label.

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.

LDY $157C,x
LDA XSPEED,y
STA $B6,x
LDA #$10
STA $AA,x
JSL $01802A
RTS
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).

LDA $77
AND #$03
BEQ NotTouchingWall
;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:


LDA $1588,x
AND #$03
BEQ NotTouchingWall
;Is touching a wall.


When it's touching a wall, we'll flip the direction using EOR.

LDA $157C,x
EOR #$01
STA $157C,x


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:

LDA #$07
ORA $64
STA $0303,y


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:


PHY
LDY $157C,x
LDA PROPERTIES,y
PLY
ORA $64
STA $0303,y

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:

LDA $157C,x
STA $02

So it's:


JSR GET_DRAW_INFO
LDA $157C,x
STA $02
;..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?

PHX
LDX $02
LDA PROPERTIES,x
PLX
ORA $64
STA $0303,y


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...

PHX
LDA $13
LSR
LSR
LSR
AND #$01
TAX
LDA TILEMAP,x
STA $0302,y
PLX

PHX
LDX $02
LDA FLIP,x
PLX
ORA $64
STA $0303,y


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:


PHX
LDA $13
LSR
LSR
LSR
AND #$01
TAX
LDA TILEMAP,x
STA $0302,y

LDX $02
LDA FLIP,x
ORA $64
STA $0303,y
PLX


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.

$1588,x 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:

DontFlip:
LDY $157C,x
LDA XSPEED,y
STA $B6,x
...

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:

DontFlip:

LDA $1588,x
AND #$04
BEQ NotOnGround

LDY $157C,x
LDA XSPEED,y
STA $B6,x
LDA #$10
STA $AA,x

NotOnGround:
JSL $01802A
JSL $018032
RTS

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 initially 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 and SUB_OFF_SCREEN_X0. 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).

dcb "INIT"
JSR SUB_HORZ_POS
TYA
STA $157C,x
RTL

..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.
Last edited on 2012-01-07 06:48:04 PM by Alcaro.

Programming A Sprite - A Tutorial (Part 2)



Lesson 8 : Interaction With Mario


Let's continue with our last lesson - the Bob-Omb sprite we were working on. The Bob-Omb moves around and interacts well, but we need to give it Mario/Sprite interaction.

Recall that I said JSL $01A7DC would make the sprite check for Mario/sprite interaction. BCC NoContact (or whatever label name you choose) means that there was is no contact.

But I am sure that in some sprites, you've directly seen a JSL $01A7DC and then an RTS, like this:

JSL $01A7DC
RTS

There wasn't any BCC there, so how would it check if Mario was in contact with the sprite?

This is where we'll learn about the "don't use default interaction with Mario" option in the .cfg editor. Default interaction means that it has the interaction of many SMW original sprites. With default interaction, Mario gets hurt if he touches the sprite from the sides (obvious..), he can stomp on the sprite (not kill, stomp), and some options in the $1656 section of the .cfg editor can be checked to add some more features.

I usually give my sprite custom Mario/sprite interaction though, which means your own code when Mario touches the sprite - like the sprite pushing Mario away, or Mario lifting it up and throwing it.

Anyway, note that if you uncheck "default interaction with Mario" in the .cfg editor, you directly JSL to $01A7DC. If you're NOT using default interaction and want to make your own Mario/Sprite touch code, you uncheck that option in the .cfg editor and add a BCC Label to return if there was no contact.

We're going to make our sprite have some custom interaction - so uncheck that option in the .cfg editor.

Add a JSL $01A7DC BCC NoContact (on 2 separate lines of course) in your sprite's .ASM file, right after JSL $018032.

Make your NoContact label branch to a return command (RTS), and write this if contact is on:

LDA #$02
STA $19
NoContact:
RTS


(Yes, it's supposed to give Mario a feather).

Test your sprite now. If Mario touches it, he becomes caped!

...but that's obviously not what we want our sprite to do when we're in contact with it. For our sprite, we're going to add the following:

- IF Mario touches the sprite from the sides, hurt him (or kill him if he's small).
- IF Mario touches the sprite from above (i.e. jumps on him), kill the sprite.


After the call to $01A7DC (Mario/Sprite contact check), $0E contains Mario's Y position - Sprite's Y position. If it's greater than E6, the Sprite will hurt Mario. We use this code to check if Mario touches the sprite from above:

LDA $0E
CMP #$E6
BPL SpriteWins


So if $0E is greater than E6, the Mario is touching the sprite from the sides. Again, if this doesn't branch, meaning $0E is less than E6, Mario is touching the sprite from above (jumping on it). So if it doesn't branch, we'll kill the sprite.

Add the above code to the sprite. Directly after the BPL SpriteWins label, place an RTS. We'll make the "MarioWins" code later.

Create a label called SpriteWins. Remember that this branches if Mario touches from the sides. First we check if Mario has a star (if $1490 is a non-zero value) and if he does, we branch to a separate piece of code that handles the star points routine. So add this code in your sprite after SpriteWins:

LDA $1490
BNE HasStar


Now if Mario doesn't have a star, we can directly JSL to the hurt routine, (JSL $00F5B7) because it's common for sprites to hurt Mario. If you want though, you could create some other code, like pushing him away. I'll just add a JSL $00F5B7 followed by an RTS to end the "Sprite wins" code because there's nothing more that's left.

We've still got to finish the "MarioWins" code and the star points routine now. Since the star points routine is fairly common and is present in many custom sprites, you can find it here. Preferably copy it above your graphics routine, so it's easier to find it. It is also commented on by me, so you can understand it too.

The star routine is now done, but we've got to do the "MarioWins" code now. We shouldn't directly use STZ $14C8,x as that just erases the sprite, which we don't want. We'll make it play some sound, some contact graphics to make it better and then fall down. So remove the RTS after the BPL SpriteWins line to put some code there:

JSL $01AB99
JSL $01AA33
LDA #$10
STA $AA,x
LDA #$02
STA $14C8,x
JSL $01802A
LDA #$13
STA $1DF9


The first JSL displays the "contact" graphics that's often shown in many sprites when they're hit. The second one prevents Mario from being launched down again after stomping on the sprite and lifts him a bit up. Then I make the sprite fall down by setting its status to 02, killed by a star, and setting a new Y speed for it so that it drops. Finally, the speed is updated because we modified it by JSL'ing to $01802A and a sound effect is also played.

The final code for the sprite should be like this. Note that I've added headers to easily separate and locate code.

Test out the sprite again. If you hit it with a star, it'll fall down and give you points. It'll hit you from the sides and you can jump on it to kill it.

When you spin-jump on the sprite though or jump on it with Yoshi, it gets killed normally. Usually for some sprites like the Rex, the sprite disappears in a puff of smoke when spin-jumped on or jumped on by Yoshi. This is fairly easy to add in to the sprite. All we do is check if Mario is spin-jumping using $140D (if it's 01, Mario is spin-jumping) or check if Mario is on Yoshi by checking if $187A is non-zero, and then branch to another piece of code that handles the spin-kill.

Because the "contact" graphics and Mario speed (first 2 JSLs) are also used by the spin-kill and Yoshi, we add a check after them:

JSL $01AB99
JSL $01AA33
LDA $140D
ORA $187A
BNE SpinKill

LDA #$10
STA $AA,x
;.. normal kill code.


I've named my label SpinKill. Add a SpinKill label after the "normal" kill routine finishes, and add this code there:

LDA #$04
STA $14C8,x
LDA #$08
STA $1DF9
JSL $07FC3B
RTS


Firstly, we set the sprite status to 04, which is being killed by a spin-jump. Then we play the typical spin-kill sound effect and lastly, JSL to a routine that displays the "star animation" effect which is common in sprites which you spin-kill on.

Test our the sprite with this additional. This should be your new code. Stomp on the sprite or jump on it with Yoshi and you'll notice that it dies differently.

We've now fully made a simple yet functioning Bob-Omb sprite. Of course, it doesn't act like a real Bob-Omb, because we're still doing basic stuff. We've added in some behaviour for this lesson which is relatively good. If you want though, you can create your own custom routines for the sprite being killed/hurting Mario as long as you know what you're doing.

Lesson 9: Creating A 16x32 Sprite


We've played around with a 16x16 sprite for a long time now, so it's time we make a bigger sprite. This one will be one tile wide but two tiles high. It will require a bit of work and change in the graphics routine, so we're going to start afresh. This template sprite will be used. After we've done this lesson, we'll make a 32x32 sprite.

To make a 16x32 sprite, we'd need to draw 2 tiles - the bottom one and the top one. Because of this, we'd need to create an loop in the graphics routine to draw both tiles. If you're already familar with loops, this won't be hard at all.

This is a routine to create a 16x32 sprite. It's well-commented on, but I might as well explain here.

Firstly, we MUST push the sprite index, as once again, we need X to indicate the number of tiles to draw -1 (which is 01 in our case, because we're drawing 2 tiles). Currently, we set X to 01. This means it will first work on the second tile (or the upper tile), and then the first tile, or the bottom tile.

We store the X position normally (keep in mind this is for the second tile and the first tile). However, we can't directly store the Y position ($01 to $0301,y). This is because we've got 2 tiles to draw and we need to put 1 tile on top of the other one. If we had stored the Y position directly, one tile would have overlapped the other one, which is what we don't want. To overcome this, we need to add some Y displacement to the sprite:

YDISP: dcb $10,$00

LDA $01
SEC
SBC YDISP,x
STA $0301,y


For the second tile to draw, which we are doing first because X was initially loaded with 01, we don't need to apply any Y displacement to the sprite. For the first tile, however, we need to shift it a bit lower than the second one. So a displacement of 10 is subtracted to make it one tile lower.



Generally, 10 makes it 1 whole 16x16 tile less. It can be changed to mess around with the displacement the first tile gets, but we really don't need to do that.

The tile data is indexed by X and stored to $0302,y. So if X is 01, we load and store the second value (tile) from the table. When X = 00 (we'll decrease it later), it will load the first value (tile) from the table. Note that my table is called TILEMAP.

Then we push X because we need to add the properties based on the direction of the sprite, which we stored to $02 earlier on. Note that it doesn't give a separate property for each tile; X here contains the direction.

After increasing Y 4 times, one tile is finally done. To do the next tile, we use DEX to make X 00 (so that the index makes it load the first byte in the tables). The loop is repeated with a BPL. If you DEX when X is 00, it'll be FF, negative. BPL only branches when positive, so it won't loop again.

We pull back the X register which we pushed RIGHT at the beginning of the routine which contains the sprite index. Finally, Y is loaded with 02 to indicate that we drew 16x16 tiles, and A is loaded with 1, which is the number of tiles drawn -1 (2-1 = 01).

Test out the sprite. You should be able to see 2 16x16 tiles; a 16x32 sprite - one tile is the walking frame of the Bob-Omb and the other one is it's still frame.

It wasn't so hard, was it? All it does it involve a little loop with some Y displacement and more indexing, but other than that, it's not hard at all. If you'd like, you can change the SEC SBC YDISP,x to CLC ADC YDISP,x and change the F0's in the YDISP table to 10. This is the exact same thing, but using addition instead.

Now our 16x32 sprite is done. A Rex has a clipping value of 2A and it's 16x32, so you can use that as your clipping value in the .cfg editor. There is no difference in the sprite's coding when you have a 16x16 tile or a 16x32 tile, it's the same. If you give the sprite some speed, both tiles will have that speed.

Drawing a 32x32 sprite is almost the same, it's just that we add some X displacement as well as some Y displacement.

Lesson 10: Creating A 32x32 Sprite


Open up your 16x32 sprite graphics routine. For a 32x32 sprite, there's not much to add except for an X displacement..

This is a graphics routine of a 32x32 sprite (non-animated, of course. We'll come to animating 32x32 sprites later). Because a 32x32 sprite requires you to draw 4 tiles, I've made our code loop thrice by making X 03 at the beginning of the loop.

Because of this, we'd need to extend our XDISP and YDISP tables to 4 bytes as well. For a 32x32 sprite, we'd need to following tiles:

- XDISP of 00 and YDISP of 00
- XDISP of 10 and YDISP of 00
- YDISP of F0 and XDISP of 10
- YDISP of F0 and XDISP of 10


I've made my table like this:

YDISP: dcb $F0,$F0,$00,$00
XDISP: dcb $10,$00,$10,$00

The 4th byte (tile) in the table would load the tile whose offset hasn't been changed at all. (bottom-right)

The 3rd byte (tile) in the table would load the tile whose Y offset hasn't changed, but it's X offset has, so it'll be shifted 1 tile to the left. (bottom-left)

The 2nd byte (tile) in the table would load the tile whose X offset hasn't been changed, but the Y offset has, so it'd be shifted 1 16x16 tile higher. (upper-right)

The 1st byte (tile) in the table would load the tile whose X and Y offset has both been changed, so it'd be shifted 1 tile to the left and 1 tile higher (upper-left)



..god, is that a fail diagram?

So we've gotten each tile it's separate position. How do we know what tile they'll load though? Simple. The tiles loaded correspond to the offsets we have. The 1st byte of the TILEMAP table would be the upper-left tile. The same goes for all other bytes.

The only other thing which we have changed again is the number of tiles drawn at the end of the graphics routine in the accumulator. We drew 4 tiles, so we load A with 4-1 = 03.

Test your sprite out. It won't move or anything, but atleast it's got a 32x32 size!

However, there's a big problem! Give the sprite some speed and flip it's direction when it hits a wall. You'll notice that when it faces the other way (right), the X and Y displacement are all messed up! The same thing would happen for a 16x32 sprite if you hadn't already realised.

To fix this problem, we need to invert XDISP (not the Y displacement, only X displacement) when the sprite faces the left direction. This code will fix our problem.

To fix it, we use some scratch RAM (RAM that can be used by anything), $03, to load along with X for the loop. We add 4 bytes to $03 if Mario faces left, so the next 4 bytes will be used for X. This means that we can extend our table by 4 more bytes and those 4 bytes are used by the sprite's graphics when it faces left.

XDISP: dcb $10,$00,$10,$00
dcb $00,$10,$00,$10


As you can see, those last 4 bytes are inversed.

This also means that our YDISP table must also have 4 more bytes, as well as the tilemap table. We just copy the first 4 bytes for them though because they aren't affected.

TILEMAP:
dcb $CA,$CC,$EA,$EC
dcb $CA,$CC,$EA,$EC

YDISP: dcb $F0,$F0,$00,$00
dcb $F0,$F0,$00,$00


Test out your sprite once again. When facing both directions, it'll show correct graphics now and we're done with the graphics routine of our first 32x32 sprite. If you'd like, you can give it some interaction code like we did with our 16x16 sprite earlier. In the next lesson, we will learn how to animate our 32x32 sprite, which is extremely difficult somewhat complicated.

Lesson 11: Creating An Animated 32x32 Sprite


We've already gone through creating an animated 16x16 sprite and it wasn't too difficult. Animating a 32x32 is a little bit harder, but the concept is still the same - you use the frame counter to swap through tiles.

Right after the call to GET_DRAW_INFO and before storing the sprite's direction into $02, add this code:

LDA $13
LSR A
LSR A
AND #$01
ASL A
ASL A
STA $03


Firstly, we load the frame counter and add a a couple of LSR's to slower the animation rate. Next, we use AND #$01 to indicate that we're animating between 2 frames (0 and 1).

Before storing the result into $03 to use along with indexing, we add a couple of ASLs. This will make the sprite:

- On it's first animation (0), use frames 1-4.
- On it's second animation (1), use frames 5-8.


The ASL multiplies the bit by 2,

1x2 = 2
2x2 = 4 Frames.

So every time the frame counter switches from animation 0 and 1, it will also change the tilemaps it will use. However, our table currently has:

TILEMAP:
dcb $CA,$CC,$EA,$EC ; ANIMATIONS 1 (BYTES 1-4)
dcb $CA,$CC,$EA,$EC ; ANIMATIONS 2 (BYTES 5-8)


As you can see, both of these animations are the same! So although it'll switch between these bytes, the tiles used will be the same until we change them. But we also can't do that, because:

LDA $03 ;\
CLC ; | If sprite faces left ..
ADC #$04 ; | Adding 4 more bytes to the table.
STA $03 ;/ So we can invert XDISP to not mess up the sprite's appearance.


When facing left, the maximum value for X is:

Max frame + no XDISP = 8 + 0 = 8

There are 8 bytes in our table, so this is not a problem.

When going left, the maximum value for X is:

Max frame + XDISP = 8+4 = 12.

We don't have 12 bytes in the table! This means, when the sprite is going left ..

TILEMAP:
dcb $CA,$CC,$EA,$EC ; FRAME 1 RIGHT
dcb $CA,$CC,$EA,$EC ; FRAME 2 RIGHT / FRAME 1 LEFT

dcb $xx,$xx,$xx,$xx

The code in red doesn't exist yet. The sprite is trying to use bytes from the table which don't exist. The part in bold is where the frame starts from when the sprite faces right.

To fix this, we can make a bigger table and expand $03 later. I've made this table:

TILEMAP:
dcb $CA,$CC,$EA,$EC ; FRAME 1 ;\ RIGHT
dcb $C6,$C8,$E6,$E8 ; FRAME 2 ;/ RIGHT

dcb $CA,$CC,$EA,$EC ; FRAME 1 ;\ LEFT
dcb $C6,$C8,$E6,$E8 ; FRAME 2 ;/ LEFT


This is what I've done. I've made the table less cluttered and made bytes 8-16 be used when the sprite is facing right. The tile data is still the same, but because $03 is being affected by XDISP when sprite faces the right direction, the frames being animated start from byte 4 and then read from an invalid table.

I have now expanded the table. This means I'll have to add 8 bytes instead of 4 when the sprite faces right:

LDA $157C,x
STA $02
BNE NoAdd
LDA $03
CLC
ADC #$08
STA $03


The part in bold is what has been changed. Now the sprite will read from bytes 8-16 for the animations instead of 0-1, so nothing will mess up.

But adding 8 more bytes also means we have to expand the XDISP and YDISP table. So make them this instead:


YDISP: dcb $F0,$F0,$00,$00 ; FRAME 1 ;\ RIGHT
dcb $F0,$F0,$00,$00 ; FRAME 2 ;/ RIGHT

dcb $F0,$F0,$00,$00 ; FRAME 1 ;\ LEFT
dcb $F0,$F0,$00,$00 ; FRAME 2 ;/ LEFT

XDISP: dcb $00,$10,$00,$10 ; FRAME 1 ;\ RIGHT
dcb $00,$10,$00,$10 ; FRAME 2 ;/ RIGHT

dcb $10,$00,$10,$00 ; FRAME 1 ;\ LEFT
dcb $10,$00,$10,$00 ; FRAME 2 ;/ LEFT


The sprite is now ready and can be tested for checking if it animates between 2 frames. In case you still haven't modified your sprite code, this is the final code. If you set SP4 to GFX20.bin, you will notice that it's a Mega Mole in blue color :P

tl;dr this is a code to animate a 32x32 sprite. I hope you actually listened, though..

Animating a 32x32 sprite with more than 2 frames is going to be more complicated, so we won't go through that because this is supposed to be a simple-intermediate sprite tutorial.

Lesson 12: Making Use Of Sprite Tables And Adding More Behaviour To A Sprite



In the last 5 lessons or so, we've just been working on changing the graphics routine, like making an animated sprite or a large sprite. In this lesson, we'll focus on how to use sprite tables that are usuable for the sprite's custom routines.

In total, there are plenty of sprite tables you can use for custom use in a sprite, like jumping every 4 seconds or perhaps a flag to indicate that it's been jumped on. The most commonly used tables are:

- $1528,x
- $1534,x
- $1510,x
- $1504,x
- $1594,x
- $1540,x
- $1558,x
- $163E,x <- This one decreases by itself every frame.
- $C2,x


There might be more than that, but these are already 9 tables for you to use! Using them is quite simple and it's almost like using free RAM. Let's say we wanted to make our sprite jump every 3 seconds. We could increment a sprite table and check if it's at a certain frame, and if so, set a Y speed for the sprite. Like this:

LDA $1534,x ; Load a sprite table ..
CMP #$CC ; IF CC frames have passed ..
BEQ Jump ; Branch to jump.
INC $1534,x ; Increase $1534,x every frame.
RTS

Jump:
LDA #$D0
STA $AA,x ; Set a Y speed for the sprite so that it "jumps"
LDA #$01
STA $1DFA ; Play "Mario jumps" sound.
JSL $01802A
STZ $1534,x ; Clear timer for jumping again.
RTS


In the above example, we made use of $1534,x - we used it as a "jump timer" for our sprite. If you want to, add this code into your sprite and test it. Every CC (204) frames, which is about 3 seconds, the sprite should perform a small jump!

Like that, you can use other tables for other stuff, but make sure that it's indexed by X to indicate that it's a table! If in the above example, we incremented $1534 only, the sprite probably won't work properly.

$1528,x is often used for a sprite's HP. In the initialisation routine, it is set to the maximum HP of the sprite, and decrements each time you jump on it. When it finally reaches 00, the sprite is killed. Of course, additional stuff can be done depending on what $1528,x is. If it's at a low value, perhaps it could move faster.

..But we're not going to create a boss sprite yet. We'll learn a lot more, like how to turn a sprite into another one, spawn extended sprites, give a sprite some health etc. Once we're finally done, we will create a boss sprite to finish this tutorial.

Changing The Sprite Into Another One



It is very possible to turn the sprite into another one - normal and custom. This is very simple to do, the sprite number is stored to $9E,x, the status (08) is stored to $14C8,x and a routine is called through a JSL to reset the sprite tables - $07F7D2. For example, if I wanted to make my sprite turn into a Bullet Bill, I could do this:

LDA #$1C ; Sprite = Bullet Bill
STA $9E,x
LDA #$01
STA $14C8,x
JSL $07F7D2 ; Reset sprite tables.


If you add that code, the sprite will turn into a Bullet Bill. To spawn a custom sprite, there are 3 things to need to do:

- The sprite number to generate is stored to $7FAB9E,x instead of $9E,x.
- An additional routine, $0187A7, is called after $07F7D2 to reset custom sprite tables.
- $7FAB10,x must be set to 08 to indicate that the type of sprite is custom.

LDA #$01
STA $7FAB9E,x
LDA #$08
STA $14C8,x
JSL $07F7D2
JSL $0187A7
LDA #$08
STA $7FAB10,x


#$01 is the sprite number that will be generated, here. The status is stored to $14C8,x, both resetting the normal and custom sprite tables is called, and finally, $7FAB10,x is set to 08 that a custom sprite is being generated. $7FAB10,x is a custom table used by Sprite Tool for:

- BIT 3 (08) is used to indicate that a custom sprite is generated.
- BIT 2 (04) is used to check if the extra bit is set.
- BIT 1 (02) is used to check the Extra Propery byte.

NOTE: It is a little awkard to see a sprite all of a sudden turn into another one, though. Some sprites often use a routine that draws smoke when generating the sprite. You can find the code here and access it through a JSR in your sprite code to generate a new sprite.

Sometimes, you might want to make the sprite spawn another sprite instead of turning into one. We'll get to that later.

Making Use Of The Extra Bit



In many sprites, you will find something like:

"USES EXTRA BIT: YES. If set, it will spawn a custom sprite".

And when you set the Extra Info to 03 in Lunar Magic, the sprite has a different behaviour. We can also make our sprite do something like that. All you do to check if the Extra Bit is set is see if bit 3 of $7FAB10,x is set:

LDA $7FAB10,x
AND #$04
BEQ BitClear

;Code activated when extra bit is set.


You can use this as many times as you want in a sprite and do different actions if it's set or clear. For example:

LDA $7FAB10,x
AND #$04
BEQ BitClear
STP ; STP the game for lulz


This code will freeze the game when the extra bit is set :P

Spawning A Sprite Relative To Mario's Position



What if.. you want to make a F.L.U.D.D sprite from Super Mario Sunshine? Or maybe the Blue Shell from NSMB? Both of these sprites require you to make them always follow Mario, and put them in such a position so they look like they're on his back or right in the center of his position.

That's simple to do. If you don't already know:

- $94 is the low byte of Mario's X Position.
- $95 is the high byte of Mario's X Position.
- $96 is the low byte of Mario's Y Position.
- $97 is the high byte of Mario's Y Position.

- $E4,x is the low byte of sprite's X Position.
- $14E0,x is the high byte of sprite's X Position.
- $D8,x is the low byte of a sprite's Y Position.
- $14D4,x is the high byte of sprite's Position.

For example, $94 would mark Mario's X co-ordinates on the screen. IF he reaches another screen, the high byte, $95 is incremented. This is the same for all addresses.

So to make a sprite be RIGHT at Mario's position, you can simply store Mario's positions (all 4 addresses) to the sprite's positions:

LDA $94
STA $E4,x
LDA $95
STA $14E0,x
LDA $96
STA $D8,x
LDA $97
STA $14D4,x


So we store the low and high byte of both Mario's X and Y position to the sprite's one. We don't store the sprite's position's to Mario's otherwise Mario would be stuck in the sprite's place, which is obviously not what we want.

You can test this code out, and the sprite will be at Mario's position all the time when the code is executed.

It is also possible to make the sprite spawn a bit higher/lower or a little bit away from Mario's position by adding/subtracting the offsets. For example, to make the sprite be placed a bit higher than Mario, you would do:

LDA $96
SEC
SBC #$10
STA $D8,x


Will make the Y position generation of the sprite higher than Mario. CLC ADC #$10 will do the opposite.

You should only shift the offsets using $E4,x and $D8,x. And when you do this, you also need to add/subtract 01 to the high byte to not make the low byte overflow depending on Mario's direction. For example, if I'm changing the Y position (like I did before), I would need to add this in:

LDA $96
ADC
CLC #$10
STA $D8,x
LDA $95
STA $14E0,x

^ BAD CODE.

LDA $96
CLC
ADC #$10
STA $D8,x
LDY $76
LDA $95
ADC OFFSET,y
STA $14E0,x
RTS

OFFSET: dcb $00,$FF

^ GOOD CODE.

NOTE: Carry flag is already set. So this will add/subtract 01, not 00. It is necessary to add 00 to the high bytes because if you are at a screen boundary, the sprite will probably be invisible. I've gone through this issue before myself.

Making A Sprite Spawn A Custom Sprite



Earlier, I mentioned that we can make sprites spawn other sprites. This is possibly by calling a routine that generates a sprite in the Y register. If our current sprite's data is in the X register and the spawned sprite is in the Y register, we can easily generate the sprite.

The routine is located at $02A9DE. Here is a sample code to make a sprite spawn another sprite:

JSL $02A9DE
BMI SlotsFull ; If sprite slots are full, don't generate.

;After this, the sprite to generate is in the Y register.

PHX ; NOTE: We need to use the X register for the generated sprite.
TYX ; So we preserve the current sprites data.

LDA #$0D
STA $7FAB9E,x ; The sprite number is stored to the custom sprite RAM Address. STA $7FAB9E,y doesn't exist, that's why we transfered X to Y.

TXY ; Transfer sprite to spawn back to Y.
PLX ; Restore X.

LDA #$08
STA $7FAB10,x ; Setting this to 08 means we are generating a custom sprite.

LDA #$01
STA $14C8,y ; Run sprite's INIT code first.

LDA $E4,x
STA $00E4,y ; X position.
LDA $14E0,x
STA $14E0,y ; X position high.
LDA $D8,x
STA $00D8,y ; Y position.
LDA $14D4,x
STA $14D4,y ; Y position high.

PHX ; Push current sprite.
TYX ; Get sprite to generate into X.
JSL $07F7D2 ; Reset sprite tables, to generate this one.
JSL $0187A7 ; Reset custom sprite tables, too.
PLX
; Restore previous sprite.

Firstly, we JSL to a code that gets the Y register with the sprite's index. If sprite slots are full, it returns.

Otherwise, we set the sprite's status to 01 (initialisation), the sprite number to $9E,x, and finally set up the position of the sprite to the exact same position of the sprite spawning this sprite. Again, you can shift the offsets around.

Lastly, we preserve the X index, transfer our sprite to X and reset the sprite tables. X is preserved temporarily to that doesn't also get turned into another sprite.

...

You should, by now, know a lot of stuff to make sprites. Practice with the previous lessons including this one, and if you feel you're good enough, let's make a simple boss sprite.

Creating A Boss Sprite


With this lesson and the knowledge you (should) have by now, we will create a simple boss sprite. It will:

- Animate between 2 frames (You should have this code from lesson 13)
- Have 5 HP.
- Get faster each hitpoint.
- Jump at intervals once the HP is 03.
- Die when it has 0 HP.

You can use this as a template sprite, with the animation already set up.

In the initialisation routine, set the sprite's health point to a sprite table. $1528,x is the most commonly used one:

dcb "INIT"
LDA #$05
STA $1528,x
RTL


If you want to, you can also make it initially face Mario.

We don't need to touch the INIT routine anymore now. In the sprite's main routine, we can use $1528,x as an index for health points after checking the direction:

LDY $1528,x
LDA $157C,x
BEQ Right
LDA LeftSpd,y
BRA DoSpeed
Right:
LDA RightSpd,y
DoSpeed:
STA $B6,x
LDA #$10
STA $AA,x
JSL $01802A
RTS

LeftSpd: dcb $00,$E8,$EB,$EF,$F4,$F8
RightSpd: dcb $00,$18,$15,$11,$0C,$08


I got the sprite's health points in the Y index. Then I check the direction, because the speed is positive when going right and negative when going left. If going left, a 5-byte table for the speeds (00, 01, 02, 03, 04, 05) respectively is stored to the sprite's X speed ($B6,x). The same goes for the right speed as well, except the speeds are inverted.

Lastly, the sprite's Y speed is set to 10 and the routine that applies our code is called. We have now made a code that gives the sprite different speeds based on it's health count. Quite simple, really. If you feel like it, add a code that skips storing the speed if the sprite is in air.

Now we'll make the sprite jump at a random interval using a custom sprite table, $1594,x.

Add this code to the sprite, preferably after the above one:

LDA $1594,x
CMP #$D5
BEQ TIME_TO_JUMP
INC $1594,x
BRA SHARED

TIME_TO_JUMP:
LDA #$C0
STA $AA,x
JSL $01802A
LDA #$01
STA $1DFA
STZ $1594,x
SHARED:
RTS


Give it some interaction code too:

SHARED:
JSL $018032 ; Interact with other sprites.
JSL $01A7DC
BCC NoContact

LDA $0E
CMP #$E6
BPL SpriteWins

;MARIO HITS THE SPRITE CODE

SpriteWins:
LDA $1490
BNE NoContact ; NOTE: Don't kill the sprite when having a star.
JSL $00F5B7
NoContact:
RTS


When Mario hits the sprite, instead of killing it, we need to decrease the hitcounter - $1528,x. Afterwards, we can check if $1528,x is 0 and if it is, branch to another piece of code that will kill the sprite.

So add this code for the "MarioWins" routine:

JSL $01AB99
JSL $01AA33
LDA #$A0
STA $7D
DEC $1528,x
LDA $1528,x
BEQ KillSprite
LDA #$13
STA $1DF9 ; SFX When jumping on the sprite.

KillSprite:
;ADD KILL SPRITE CODE HERE


In the KillSprite code, add whatever code you want to use when killing the sprite. A simple way to kill the sprite would be to set a positive X speed (below 80) to $AA,x and set the status to killed by star.

..And a simple boss sprite should be then made. It's very simple, because all it does is has 5HP, moves around and jumps. You can create a complex boss sprite by adding more routines when you need to and make use of sprite tables for your boss's purposes. Test the sprite out, and it should behave like I described. If it went successful, try adding a few more features to the sprite and give it more health.

It's not necessary that you must make a sprite boss sprite though. Our first proper sprite lesson covered how to make a small Blue Mushroom sprite if you recall. You can create custom powerups in this way too, but for now, this tutorial has already explained a lot. It'll get updated at some point.

tl;dr


I have spent AGES working on this tutorial, and I hope you make some use out of it. Of course I haven't covered every single thing, like making an animated sprite (yet), but I might come to that later on.

Any questions/suggestions/comments etc. are all welcome! Please ask if you have any! Also, if you find any lesson difficult to understand, please tell me. I'll rewrite it, because I personally think I've complicated some of them (the 16x32 and 32x32 lessons are crap, to be honest, I'll rewrite them someday).

Update 16th September 2010: Fixed broken sprite links, made a new pic for the 1st graphics page for sprites and changed the 0x2yy to 0x3yy (since the new LM adds one more page for the FG/BG.
Last edited on 2010-09-16 07:05:19 AM by Nesquik Bunny.
So far, I've found this tutorial very easy to understand, you've done a great job on it. I'm at the part before you give the sprite interaction, and I have but one question. If setting yxppccct in the asm prevents the palette and GFX page from being set via cfg, and the PHX LDX $15E9 LDA $15F6,x PLX ORA $64 STA $0303,y must be used to allow palette and GFX page to be set via cfg file, how does one set the yxpp?
Wow. Just wow.

Although it's not perfect (hey, we're only human), I sure wish this tutorial had been around when I was learning how to make sprites.

I do have a couple minor nitpicks, though. First of all:

Originally posted by Iceyoshi
- BIT 4 (08) is used to indicate that a custom sprite is generated.
- BIT 3 (04) is used to check if the extra bit is set.
- BIT 2 (02) is used to check the Extra Propery byte.


Don't you mean bits 3, 2, and 1 rather than 4, 3, and 2? Also...what does bit 1 have to do with extra property bytes? Doesn't it indicate if the sprite has been initialized or not? And what about bit 7, which I also see in some sprites (LDA #$88 STA $7FAB10, for example)?

Secondly:

Originally posted by Iceyoshi
- $1528,x
- $1534,x
- $1510,x
- $1504,x
- $1594,x
- $1540,x
- $1558,x
- $163E,x <- This one decreases by itself every frame.
- $C2,x


Although I won't get on your case for not including some of the other misc. sprite tables like $1602,x, $160E,x, and $1FD6,x (after all, you did say that there were more), I have to say that I'm pretty sure $1540,x and $1558,x also decrement automatically.

Overall, though, this is a pretty nice tutorial.
Originally posted by Iceyoshi
Notice the Pass 1, 2 and 3. Mainly, we refer to Pass 3 for errors.

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.


This is the most awesome tutorial ever. One question though, what is the number 8.1 for in the error?
Ultimaximus: You can set the priority yourself by replacing the ORA $64 with nothing at all, ORA #$10, ORA #$20, or ORA #$30, depending on what priority you want the sprite to have. As for the yx bits, ORA #$40 will set the x bit, and ORA #$80 will set the y bit. If they already happen to be set, AND #$BF will clear the X bit, and AND #$7F will clear the Y bit.
Glad to see this is finally out. It helped me a TON, and should help others. I vote sticky.
Originally posted by The Ghostly Kirby
Glad to see this is finally out. It helped me a TON, and should help others. I vote sticky.

What use is a sticky in a small tutorial forum? It's not going to be buried too deep, probably even in a year.

hmm, it looks like you never put the JSR SUB_OFF_SCREEN_X0 in the first sample, despite explaining it.
Originally posted by Ultimurdermus
Originally posted by The Ghostly Kirby
Glad to see this is finally out. It helped me a TON, and should help others. I vote sticky.

What use is a sticky in a small tutorial forum? It's not going to be buried too deep, probably even in a year.

hmm, it looks like you never put the JSR SUB_OFF_SCREEN_X0 in the first sample, despite explaining it.


Well, just in case we get a bunch.
Thank you! I'm so happy someone finally made a tutorial specifically geared towards sprite programming. Not all of us are ASM savvy enough to figure it all out without proper instruction at least covering the basics. Thank You! ^_^
Thanks guys, I spent days working on it, and I'm glad it was worth the time.

Ultimaximus: To set the YXPP part, you need to set those bits manually, using ORA. In YXPPCCCT, Y is the last bit, so I'd need to use ORA #$80:

PHX ; Push X if necessary.
LDX $15E9
LDA $15F6,x
ORA #$80 ; <- Include Y flip.
ORA $64
STA $0303,y
PLX

Similarly, I'd use ORA #$40 for the X flip.

lolcats439: I'm not sure, but I think it's the instruction number on the current line which has the error. Normally though, this is unimportant so you don't need to care about it.
Thanks Iceyoshi! I've been wanting to make my own custom sprite, and now I know how! Now I can get to making my own enemies!
Fantastic tutorial, you've clearly put a lot of work into it, and it shows, it's informative and useful, but there is a problem in the interaction section


LDA #10
STA $13CC ; Add 10 coins flag
LDA #$01 ; Some sound effect
STA $1DFC <---- this is incorrect, the value you have in the example code is 1DF9, after trying both, 1DFC causes a crash 1DF9 is correct
$1DFC shouldn't cause a crash because it's also a music port. But thanks for pointing that out, I'll go fix it.
Thanks a lot Iceyoshi. I not only appreciate this tutorial, but your patch making tutorial and all the sprites and patches you've made for all of us as well. Really, thank you so much.
Originally posted by Iceguy
Simple. The OAM is in the Y index. To make 8x8 become 16x16, we just increment it 4 times:

INY
INY
INY
INY


Incrementing Y four times has nothing to do with tile sizes.

One tile uses 4 OAM bytes. If you draw two tiles, it uses 8 OAM bytes. If we want to draw the 2nd tile, we will have to get the next OAM slot. Hence the four INY. If you drew two tiles, and you didnt use INY four times, you would overwrite the first tile, rendering it useless.

Use INY four times ONLY IF you are writing more than one tile.
Last edited on 2009-10-24 04:03:25 PM by 3spooky5me.
Oh, okay. I must have misread mikeyk's tutorial.

Fixed.
Wow, this is a very good tutorial. :]
I will deffinitely use it some time, kudos. :D
This is a great tutorial but I found a small mistake that isn't really a big deal just bugged me.

Code
dcb "INIT"
RTL

dcb "MAIN"
LDA $0DBF
CMP #10
BCC Return
LDA #$02 ;GIVES CAPE
STA $19
Return:
RTL


Originally posted by Iceyoshi
Once you reach there, Mario should become fiery if he's got atleast 10 coins. If not, nothing happens (This is assuming you've written my sample code above).


As I said not a big mistake but still might want to fix that.
Great tutorial! This has really helped out a lot and is so easy to understand. I can finally do more than just basic sprite tweaks.

Question though:
Code
LDA $9D	 ;\
BNE RETURN	;/ If sprites are locked, return.

Originally posted by Iceyoshi
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


Does this also cover the animation frames? If i understand correctly, the code shouldn't run when sprites are frozen as it just jumps to an RTS. All is fine except the animation frames continue. I hope I'm just overlooking something blatant.

Other than a slightly modified speed table, spinjump routine and mariowins routine, right now my sprite is exactly the same as the tutorial's up through step 8.

EDIT: Never mind, found the problem. In the animation routine, the tutorial has LDA $13. After looking at another custom sprite that I've been using and comparing to the RAM map, I see that $14 (labeled as the sprite frame counter in the RAM map) will freeze the frame timer when sprites are frozen whereas $13 will not. I knew it had to be something obvious that I just kept overlooking - jut never saw it because I kept comparing back to the code posted here in the tutorial.
I know it's nitpicky, but you might want to fix that in the tut.
Last edited on 2009-10-26 11:43:51 PM by Milk.
Pages: « 1 2 3 4 5 6 ... 8 9 »
Forum Index - SMW Hacking - General SMW Hacking Help - Tutorials - Sprite Programming

The purpose of this site is not to distribute copyrighted material, but to honor one of our favourite games.

Copyright © 2005 - 2014 - SMW Central
Legal Information - Privacy Policy - Link To Us


Total queries: 27

Menu

Affiliates

  • Talkhaus
  • SMBX Community
  • GTx0
  • Super Luigi Bros
  • ROMhacking.net
  • MFGG