Main principles of creating an "object"

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Post Reply
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Main principles of creating an "object"

Post by Drew Sebastino »

So, I think I'm at the point where I can ask this question. What I mean are what are the steps to creating an object like gnawty from DKC? There are many things I can think of, like actually programming the object (how it walks and attacks, and this of course includes collision detection,) looking to see if anything shares the objects palette, looking for an empty vram slot, keeping track of where it is in vram and updating the tiles, (you would have to make an animation engine) and actually making it through a metasprite making routine and other things. (this would probably go last.) I'd just like to know a good way to link everything together. I also kind of wonder how you would have multiple objects on screen at once and not interfere with the other objects. I mean like having an x and y position for every object and maybe a general purpose register for a timer and whatnot. I guess you could set it up like a sprite buffer or something like that, but how would you remember what registers go to what object? Grr...
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: Main principles of creating an "object"

Post by psycopathicteen »

Let's say that in your game, each object has an "x_position" register, a "y_position" register, and a "metasprite_data_address" register. You can define each of them with an 8-bit address.

x_position = $00
y_position = $02
metasprite_data_address = $04

If you set the Direct Page to $0200 like this:

lda #$0200
tcd

Your object controlling code would work, except it will read and write from $0200, $0202 and $0204, instead of $00, $02 and $04. You can have multiple sets of identical registers by changing the Direct Page. You can do this:

lda #$0200
tcd
do "object A" stuff here
lda #$0280
tcd
do "object B" stuff here
lda #$0300
tcd
do "object C" stuff here
User avatar
whicker
Posts: 228
Joined: Sun Dec 13, 2009 11:37 am
Location: Wisconsin

Re: Main principles of creating an "object"

Post by whicker »

but how would you remember what registers go to what object?
I think what you're getting at is, how do you manage these objects?

With a list.
Then you stop caring about what position it is in the list, just that it is in there somewhere.


So you'd have a method to 1) clear the entire list, 2) add an object to the list (or signal an error if it's full, or silently overwrite the oldest entry, or the last slot, or whatever). And 3) a method to remove the object from the list (mark the item in the list as unused).

When it comes time to process the list, additional code would look at first of all what kind of thing is in this slot, with something like value 0 meaning skip and look immediately at the next slot as it's not used. or value 1 could be goon enemy, and value 2 could be bird enemy, value 3 is an explosion that animates for a specific series of frames, etc.

Based on that "kind of enemy" variable, call the code that processes it using a jump table with kind of enemy as the index (or more easily with a series of comparisons and conditional branches (if equal to that value branch here)), that code process the enemy logic, then have it return back so the next item in the list can be processed, until everything in the list has been looked at, then move on.

When calling the object handling code, you would need to pass it a memory address pointer (in a register, on the call stack, or in a specific fixed memory location, or another index) to the memory area in this list that the object handling code can modify and reference. So things like the X position, Y position, internal-state, name (if it matters), and anything else you can think of are referenced offset off of this memory pointer. (let's say internal state is a word at my_memory_pointer+6).



As to how long the object list is, or how many variables need to be in each slot in the list, that's totally dependent on the worst case of what the game design demands. Also it would be wise for debug code for complain that the list is too small, to catch the errors during play testing.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Main principles of creating an "object"

Post by Drew Sebastino »

I think I remember koitsu saying that the ca65 assembler always has the index register set to 0, so I won't have to worry about that now. I'm wondering if maybe you would need to have a table in ram that would store different things for each objects like:

Object 1:

x_position = $00
y_position = $02
metasprite_data_address = $04

Object 2:

x_position = $06
y_position = $07
metasprite_data_address = $08

Object 3:

x_position = $0A
y_position = $0C
metasprite_data_address = $0D

etc...

I guess whenever you spawn a new object by looking at a table when scrolling (which I think someone explained to me how, but I don't remember...) you would have an incrementing pointer so you don't just write to the first object every time. I was wondering how you would tell what each object was by its slot in the table, but I guess you could have an "object identification" byte, (or bytes) that would say what each object is, like if it's a Koopa or a Goomba or something like that. How many slots for objects do you think I should have? I'd say you could have 128 because this grantees you have at least the same number of objects as sprites, but this would be very circumstantial but if every object somehow only used 1 sprite, they could all still show.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Main principles of creating an "object"

Post by tokumaru »

I don't write SNES code, most of the logic involved would be the same no matter the system, so I'll share some of the information I find important.

Like whicker said, you should maintain a list (or an array) of objects. How many bytes per object is up to you, and depends on how much state you want to keep track of. Each object has a type, a position, physics data, graphics pointers, counters, and so on, so be sure to pick a size that will comfortably hold all the data you need. Keep in mind that if only a couple of objects (such as bosses) are overly complex and need significantly more RAM, you can have it occupy more than one object slot, instead of making each slot huge and hardly use the space most of the time. Not such a big problem on the SNES, which has quite a bit of RAM, but still.

Whenever you need to load a new object, be it from a list of objects in ROM or a dynamic object (like an explosion), just look for an empty slot (scan the list looking for an invalid object type, which acts as a flag to indicate that the slot is free) and put it there. Set its type, position and anything else necessary to get the object started. Similarly, when the object is done, just have it overwrite its type with the "empty" flag and the slot is free again.

To process the objects, scan the object list every frame, and whenever you find a valid object type, use that to jump to the object's AI routine. A jump table would be the best option here. Accessing the chunk of RAM reserved for each slot can be done in different ways. On the NES, we can use indirect (a pointer indicates the beginning of the RAM chunk) or indexed (the X register indicates which slot is being used, and the attributes are interleaved) addressing, and I'm sure the SNES provides even more ways for you to do that. There's not a single right way to do it, so use what makes you more comfortable.

The important thing is that the AI routine always manipulates the chunk of RAM that belong the current object. This will allow your objects to work from whatever slot they're loaded in. You can also write functions that can be reused for several objects. This is useful for physics, metasprite rendering, and all sorts of tasks that are common for many objects. Just call these functions from within the object's AI code and they'll also work on the appropriate chunk of RAM.

About interactions between objects: most of them need to interact with the player, so it makes sense to put the player at a know position in the list (the first position is a good candidate), so you can have the other objects call functions like "TestCollisionWithPlayer" and these routines know where to find the player. Object management gets a little more complicated when more objects have to interact with each other. In this case, it helps to have separate lists of objects: one for projectiles, another for enemies, and so on, depending on which groups interact with which. This will allow enemies to call a "TestCollisionWithProjectile" routine that will scan only the list of projectiles looking for a hit.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Main principles of creating an "object"

Post by tokumaru »

Espozo wrote:How many slots for objects do you think I should have? I'd say you could have 128 because this grantees you have at least the same number of objects as sprites
I think 128 is overkill, and there's no way in hell you'd be able to update 128 objects with physics and collisions and keep the game running at 60fps. The exception would be a bullet hell shooter, but in most other cases you won't get anywhere near to 128 active objects. You'll be scanning this list quite frequently, so making it exceedingly long might still impact performance even if you're not using most of it most of the time.

You could build a couple of linked lists to help you manage the main object list (I do this in my NES projects). One of them lists empty slots and the other lists slots in use. This makes loading objects faster (no need to scan the list, just claim the first unused slot and remove if from the list of unused slots). Processing objects is also faster, because you only need to check the slots that are listed as used, instead of checking them all and looking at the object type.

If you do decide to have as many object slots as 128, I'd say it's essential that you have these linked lists to help you manage them.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Main principles of creating an "object"

Post by Drew Sebastino »

When you say "linked list" do you mean a list that that says what slots in the list are used and what aren't? Couldn't you just have it to where you check to see if the "object identification" byte in each slot is 0, meaning that the slot is empty and you can just jump to the next one? Also, I am planning on making a run and gun game (which I will never finish) and I want 128 objects for the worst possible scenario. I really think there could be something like 128 of something like explosions and the game wouldn't slow down too much, if at all, because all you would need to do is animate the explosion every couple of frames and build the metasprite. You wouldn't have to do collision detection or any real programming aside from knowing when to update vram and when to "kill" the object.

You know, about bosses taking up extra slots, I think DKC actually does something like this, because if you move Very Gnawty in a place he wasn't designed to be in, he plays his hopping animation, but he doesn't even move and he doesn't take damage either. I guess that the bytes that control his movement and health meter are being used for something else or other, so he is effectively messed up. Other bosses act in a similar manner.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Main principles of creating an "object"

Post by tokumaru »

Espozo wrote:When you say "linked list" do you mean a list that that says what slots in the list are used and what aren't?
Yes. A liked list is a list where each element points to the next. For example, a variable would indicate that slot 35 is the first free slot. Then, inside slot 35, you'd find the index of the next free slot, and so on, until the last free slot, that has an invalid index signaling its the last.
Couldn't you just have it to where you check to see if the "object identification" byte in each slot is 0, meaning that the slot is empty and you can just jump to the next one?
Sure you could, but when you have 128 slots this might start to have an impact on the performance of the game. Each time you load a new object (which will happen as the player or the enemies fire their guns or the level scrolls), you'll have to scan the list looking for a free slot, and this could happen multiple times in the same frame. With a linked list you can just look at the variable that indicates the first free slot and you immediately know you can use the slot it indicates. To remove it from the list (since you'll be using it), all you have to do is read the index of the second free slot (which is stored somewhere in the slot you just claimed) and store it in the variable that indicates the first free slot. This means you're gonna make use of the first free slot and the second free slot becomes the first. This is really quick and you don't have to do any scanning.
You wouldn't have to do collision detection or any real programming aside from knowing when to update vram and when to "kill" the object.
I guess that "dumb" objects like these could coexist in large numbers, as long as you don't animate them all at the same time. I still doubt you'll ever reach 128 though. =)
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Main principles of creating an "object"

Post by Drew Sebastino »

If checking to see if a byte is 0 or not is such a big burden, how would you have it to where you go where depending on what the "identification" byte is? I doubt you would check a single byte 256 times just to find out where to go. I guess you could load the "identification" byte into y, and jump to a location specified by a table that is being indexed by y? Would this work, because it appears to make sense but I've never tried this before, so I don't have a clue.

Could you have something like this and have each name be a value that says where to jump to?

dw #nothing,#goomba,#koopa,etc...
I guess that "dumb" objects like these could coexist in large numbers, as long as you don't animate them all at the same time. I still doubt you'll ever reach 128 though. =)
Well see... :twisted: (there's probably going to be plenty of overdraw problems, but that's a different story.)
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: Main principles of creating an "object"

Post by psycopathicteen »

There is the instruction "jsr (abs,x)."

The 128 slots thing, I had trouble with that. I find dp slots more convenient than index slots, except that the direct page has to be in bank 0, and you're limited to 8kB.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Main principles of creating an "object"

Post by koitsu »

Espozo wrote:I think I remember koitsu saying that the ca65 assembler always has the index register set to 0, so I won't have to worry about that now.
I'm not sure what you're referring to here. Index registers (X and Y) are set to whatever you set them to (via ldx/ldy). On power-on/reset the CPU sets them to 0. But none of that has to do with the assembler. Can you shed some light on this?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Main principles of creating an "object"

Post by tokumaru »

Espozo wrote:If checking to see if a byte is 0 or not is such a big burden
It normally isn't, but if you do it several hundred times per frame then it may start to become a problem.
how would you have it to where you go where depending on what the "identification" byte is? I doubt you would check a single byte 256 times just to find out where to go. I guess you could load the "identification" byte into y, and jump to a location specified by a table that is being indexed by y?
That's the general idea behind a jump table, but I'm not sure about the 65816 specifics, since I'm a 6502 guy! =)
dw #nothing,#goomba,#koopa,etc...
Sure, that's the idea. You don't need the # because these are not immediate values, but addresses. Also, you don't need to declare an address for the null/nothing object, you can start from the first valid object and read the table using JumpTable-2, y instead if JumpTable, y. Sounds like a stupid optimization, but why waste space you don't have to? =)
psycopathicteen wrote:There is the instruction "jsr (abs,x)."
This is perfect for a jump table! Just use jsr (JumpTable-2, x) and you won't need to waste any bytes because of the null object.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Main principles of creating an "object"

Post by Drew Sebastino »

koitsu wrote:I think I remember koitsu saying that the ca65 assembler always has the index register set to 0, so I won't have to worry about that now.I'm not sure what you're referring to here. Index registers (X and Y) are set to whatever you set them to (via ldx/ldy). On power-on/reset the CPU sets them to 0. But none of that has to do with the assembler. Can you shed some light on this?
Just forget what I said completely, as I don't have a clue as to what I'm talking about. :roll:
There is the instruction "jsr (abs,x)."
Well, yeah. I thought that that was the only way you could do it. This is how you'd write it correct?

Code: Select all

ldy  ObjectIdentificationRegister
jsr  (would a # go here?)ObjectIdentificationTable,y
93143
Posts: 1371
Joined: Fri Jul 04, 2014 9:31 pm

Re: Main principles of creating an "object"

Post by 93143 »

I think you mean direct page - ca65 is apparently 6502-oriented, and doesn't know that the zero page can be relocated on a 65816...
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Main principles of creating an "object"

Post by Drew Sebastino »

yeah... You're right. :oops:
Post Reply