So I've been working on a game and I'd like to add enemies. From what I understand, to "get rid of a sprite", you just set it's y position to #$FE. And I'm assuming I'd load a screen's respective sprites in a similar fashion to loading the screen itself:
An address table of the byte tables of sprite oam for each room. Maybe a byte table for the number of enemies on each screen. The sprites for a given room are indirectly loaded to OAM until the number for how many sprites to load is reached. Right?
But I guess I'm a little confused on how metasprites are "destroyed". Say the player shoots the 4th sprite of a 4-sprite metasprite. How should I go about telling the program "Okay, get rid of these 3 other sprites too"?
Also how would I tell the game which sprites do what? Like B Enemy should do C, D Enemy should do E. When you touch powerup F it shouldn't take away health, but disappear, and some variable be set to 1?
"Creating" and "Destroying" sprites
Moderator: Moderators
- rainwarrior
- Posts: 8062
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: "Creating" and "Destroying" sprites
I just rebuild the entire OAM buffer every frame, so there's nothing to eliminate. I just add any sprites that need to appear that frame, in a pseudo-randomized order to create priority flicker.
Re: "Creating" and "Destroying" sprites
You're thinking at a very low level. The game engine should not be concerned about OAM entries. The game engine is supposed to manage (spawn, destroy, move, etc.) objects, not OAM entries. That's what a metasprite system is for. Most games just call a function that draws each object, and this function is the one that inspects the object and uses its position and other attributes to generate all the OAM entries necessary to draw it. If the object has been destroyed and doesn't exist anymore, there will simply be no request to draw it.
Abstracting these hardware details from the game engine is essential if you want to be able to build more complex games. Directly managing OAM entries is OK for pong and other simple games, but once you have scrolling, dynamic objects and other more advanced things, it becomes nearly impossible to manage the hardware aspects in such a hardcoded way. You'll go crazy if you try.
The sub-systems of a game are meant to turn the hardware into a "black box", so that the engine can communicate with it at a higher level. A scrolling system, for example, allows the game engine to request that a specific column of the level map be rendered to the screen, without having to worry about name tables or attribute tables. The sub-system will take care of that.
Abstracting these hardware details from the game engine is essential if you want to be able to build more complex games. Directly managing OAM entries is OK for pong and other simple games, but once you have scrolling, dynamic objects and other more advanced things, it becomes nearly impossible to manage the hardware aspects in such a hardcoded way. You'll go crazy if you try.
The sub-systems of a game are meant to turn the hardware into a "black box", so that the engine can communicate with it at a higher level. A scrolling system, for example, allows the game engine to request that a specific column of the level map be rendered to the screen, without having to worry about name tables or attribute tables. The sub-system will take care of that.
Re: "Creating" and "Destroying" sprites
I'm still pretty new to programming for the entertainment system, how exactly would I implement an object system like this?tokumaru wrote:You're thinking at a very low level. The game engine should not be concerned about OAM entries. The game engine is supposed to manage (spawn, destroy, move, etc.) objects, not OAM entries. That's what a metasprite system is for. Most games just call a function that draws each object, and this function is the one that inspects the object and uses its position and other attributes to generate all the OAM entries necessary to draw it. If the object has been destroyed and doesn't exist anymore, there will simply be no request to draw it.
On the low end should I have a variable that keeps track of where in RAM it's free to draw new sprites, and on the high end have different subroutines for each object like DrawGoomba and DrawKoopaTroopa that already have their sprites and attributes hard-coded into the routines, and then just have their coordinates for each screen in .db's? Or am I going about this entirely wrong?
I appreciate your help by the way.
Re: "Creating" and "Destroying" sprites
NES, Commodore 64, Atari 7800, though these systems have very different display list strategies, it's all the same at the game logic level. Keep a separate array of "actors", each with its own (X, Y) coordinates, and run all physics on the actors. For example, the player character is an actor, the enemy is an actor, each bullet is an actor, and each explosion is an actor.
After you have moved everything, translate those actors into entries in whatever passes for a display list on a particular platform.
Unless your actors are very different in nature, such as missiles vs. explosions vs. smoke particles, you usually don't hardcode different actor types into separate routines. Instead, you can make a lookup table of (X offset, Y offset, tile number) pointers and read metasprite definitions from that.
After you have moved everything, translate those actors into entries in whatever passes for a display list on a particular platform.
- On NES, you'd take one actor and turn it into a bunch of OAM entries at relative X, Y positions from the actor's position. You guessed right that you keep track of how much space is left in the local copy of OAM so that you can add more hardware sprites as needed.
- On 7800, the screen is divided into several horizontal strips called zones. You write the actor's sprite into the display list for each zone that the actor covers. For the top and bottom zones, use the "holey DMA" bit to skip the areas above and below the actor.
- On C64, you'd take each actor and turn it into a job in your raster interrupt job table to move one or two of the 8 hardware sprites on the scanline just before the top of the actor is reached. Some games have a job for each sprite, while others use 20-line-tall "zones" and spit sprites into those zones as needed, and the simplest games just use one set of 8 sprites for the whole screen.
Unless your actors are very different in nature, such as missiles vs. explosions vs. smoke particles, you usually don't hardcode different actor types into separate routines. Instead, you can make a lookup table of (X offset, Y offset, tile number) pointers and read metasprite definitions from that.
- rainwarrior
- Posts: 8062
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: "Creating" and "Destroying" sprites
This is a very simplified example of how to draw a sprite:
There are a bunch of other things you probably want to do in your sprite routine (this is far from complete), but the basic concept is to keep a variable that tells you where in the buffer to add each new sprite (in this example "oam_pos"). Each time you call this function, a new sprite gets added to the end of the list. This way you can build your sprites in whatever order, or as many as you need.
Code: Select all
; variables:
oam: page-aligned 256 byte OAM buffer
oam_pos: starts at 0 at the beginning of the frame, fills up as sprites are added
sprite_ptr: 2-byte pointer to metasprite data
sprite_x: X coordinate for metasprite
sprite_y: Y coordinate for metasprite
draw_metasprite:
sprite_ptr = pointer to metasprite data looked up from table
ldy #0
ldx oam_pos
@tile_loop:
lda (sprite_ptr), Y
iny
cmp #END_OF_METASPRITE
beq @loop_end
clc
adc sprite_y
sta oam+0, X ; store Y coordinate
lda (sprite_ptr), Y
iny
sta oam+1, X ; store tile
lda (sprite_ptr), Y
iny
sta oam+2, X ; store attribute
lda (sprite_ptr), Y
iny
clc
adc sprite_x
sta oam+3, X ; store X coordinate
inx
inx
inx
inx ; advance to next 4-byte OAM entry
jmp @tile_loop
@loop_end:
stx oam_pos ; remember position for next sprite
rts