"Creating" and "Destroying" sprites

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
User avatar
Sogona
Posts: 186
Joined: Thu Jul 23, 2015 7:54 pm
Location: USA
Contact:

"Creating" and "Destroying" sprites

Post by Sogona »

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?
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: "Creating" and "Destroying" sprites

Post by rainwarrior »

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.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: "Creating" and "Destroying" sprites

Post by tokumaru »

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.
User avatar
Sogona
Posts: 186
Joined: Thu Jul 23, 2015 7:54 pm
Location: USA
Contact:

Re: "Creating" and "Destroying" sprites

Post by Sogona »

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.
I'm still pretty new to programming for the entertainment system, how exactly would I implement an object system like this?

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.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: "Creating" and "Destroying" sprites

Post by tepples »

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.
  • 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.
You might not understand the other platforms' display paradigms, but my point is that actor movement usually doesn't need to be tightly coupled to displaying.

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.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: "Creating" and "Destroying" sprites

Post by rainwarrior »

This is a very simplified example of how to draw a sprite:

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