This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

OAM cycling

OAM cycling
by on (#25908)
I'm writing a cycling routine for a sports game engine I've been working on. There can be up to 45 8x16 sprites on any given scanline at any given time (though not very likely). Of those, 44 are for players and need to be cycled.

So my idea was to have 2 separate $58 byte "pages" within the OAM page. For every frame, toggle which team uses which page (so priority between teams changes) as well as keep an index on what player to start the copy (from player struct ram to OAM ram) from. The index would be increased on every frame so priority among teamates changes too. The effectiveness has yet to be tested (the ai hasn't progressed far enough).

I was just wondering what techniques others have used/seen.

by on (#25910)

by on (#25911)
loopy wrote:
An easy way to do OAM cycling is to change the OAM address (2003) before each DMA.

Which means that you can't easily use sprite 0 overlap for scrolling, so if you have a status bar showing the score, you need to upgrade your board to at least MMC3.

Now what sport has 44 moving objects? Is it soccer with 16x16 pixel (double width) sprites, or gridiron football with 16x16 pixel sprites?

by on (#25914)
tepples wrote:
[...] or gridiron football with 16x16 pixel sprites?


that would be it, with the american rules. i aim to keep it easily adaptable to canadian and arena football (and possibly rugby).

by on (#25957)
loopy wrote:
An easy way to do OAM cycling is to change the OAM address (2003) before each DMA.

Has this even be tested on real hardware ? Some games, including Final Fantasy, does not touch $2003 at all. Most games just write zero to it once at startup or once before each sprite-DMA.
Having two pages for sprites is doable, and Kirby's Adventure does this. I, however, find this is an ineficiant way to do things : You calculate your sprites for two frames and write it in twice as much RAM as the normal way would (use the same page, but just cycle the sprite inside it).
To actually cycle the sprites there is several methods : Just reverse the order, place some sprite in higher priority then get the other in an order that is reversed each frames, or just place the priorities randomly, etc...
A method I found that is great is instead to have your sprite index start at 0 and increase by 4 each sprite you fill in the OAM buffer, increase it with any multiple of 4 that is not a multiple of 8. This will fill the sprites in a different order, and each time it will still use all the 64 available. Additionally, this allows you to always start at zero to use sprite-zero hits, and have all 63 other sprites follow a "random" order.

by on (#25962)
I'm still having a hard time figuring out how to cycle sprites while still keeping some of them properly layered (on top of each other).

I have thought about cycling objects instead of sprites, so I'd process them in "random" order, starting at a "random" location in the OAM buffer, but render full objects at once, using sequential sprites for the same object, in a way that layering will be respected.

The problem occurs when wrapping from the end of the buffer back to the beginning in the middle of an object, in which case layering will not be respected (sprites that should have lower priority will end up with higher priority). The solution could be checking if there is enough space for an object's sprites before the end of the buffer, and if there isn't, not use that space and go back to the beginning prematurely, wasting a few sprite slots. Yeah, it sucks having to waste slots, but looking for another object that will fit in the avaliable space will just take too much time.

Anyway, I still have to test all of this out. I'll let you know when I find a good way to do this.

by on (#25963)
Quote:
I have thought about cycling objects instead of sprites, so I'd process them in "random" order, starting at a "random" location in the OAM buffer, but render full objects at once, using sequential sprites for the same object, in a way that layering will be respected.

No, as long as you maze your objects in a "random" order there is absolutely no reason to start at a "random" location in the OAM buffer, on the countrary, I'd recommand start at zero and increase until the buffer is full if you want to do any layering.
Of course the total avoidement of layering can allow slightly better sprite cycling techniques. Another option is to arrange your layering so that the sprite will look the same regardless of the order (i.e. sprites only overlap on transparent areas), but this is not always possible if you want to keep the tile count as low as possible.

by on (#25965)
Bregalad wrote:
No, as long as you maze your objects in a "random" order there is absolutely no reason to start at a "random" location in the OAM buffer

Yes, there is. In my game, objects output their sprites as they are processed. Thing is that each object may affect the position of the player, so the player sprites are always output last. If I always started from 0, the player would always have the lowest priority. There are also other things, such as the hud, that don't count as objects so they're not cycled. Starting from a "random" location will help cycle the player and the hud.

Quote:
Another option is to arrange your layering so that the sprite will look the same regardless of the order (i.e. sprites only overlap on transparent areas)

Yes, I thought about that too. This is possible sometimes (should be OK for the player), but say I have a piece of machinery exploding, all made from sprites. Explosions should be displayed on top of the object, or else there'd be no point in having an explosion. Having a copy of the object's graphics with holes for the explosions is completely unacceptable and would impose many limitations.

by on (#25967)
tokumaru wrote:
Yes, there is. In my game, objects output their sprites as they are processed. Thing is that each object may affect the position of the player, so the player sprites are always output last. If I always started from 0, the player would always have the lowest priority. There are also other things, such as the hud, that don't count as objects so they're not cycled. Starting from a "random" location will help cycle the player and the hud.

Why not start always at position 63 and then go backwards to zero ? That way the player and the hud will always have highest priority.
Quote:
Yes, I thought about that too. This is possible sometimes (should be OK for the player), but say I have a piece of machinery exploding, all made from sprites. Explosions should be displayed on top of the object, or else there'd be no point in having an explosion. Having a copy of the object's graphics with holes for the explosions is completely unacceptable and would impose many limitations.

Of course. Then make explosions always have higher priority (that's what I do in my current project) or made the object dissapears at soon as it explodes, being replaced by the explosion instead of being behind the explosion (like in Contra).

by on (#25972)
Bregalad wrote:
Why not start always at position 63 and then go backwards to zero ? That way the player and the hud will always have highest priority.

Because they use a lot of sprites, and I do wish them to flicker in favour of other things as well.


Quote:
Of course. Then make explosions always have higher priority (that's what I do in my current project) or made the object dissapears at soon as it explodes, being replaced by the explosion instead of being behind the explosion (like in Contra).

Yeah, usually it is done like this... but there are cases when things explode but don't vanish... have you seen what happens to Robotnik after Sonic hits him 8 times?

by on (#25990)
Quote:
Because they use a lot of sprites, and I do wish them to flicker in favour of other things as well.

It's never a really good idea to have the player use too many sprites I think, but it's up to you.
Quote:
Yeah, usually it is done like this... but there are cases when things explode but don't vanish... have you seen what happens to Robotnik after Sonic hits him 8 times?

No, I have not.

It looks like you're doing everything in order to make things harder. I'd still recommand doing the following (that's what I'm doing in my game and it works well) :
- First call the inteligence routine of all objects, including player and everything, and do not maze the sprites at all.
- Have a routine sort the different objects in function of what they are to be sorted.
- Have a routine that maze the sprites of the objects in function of the data given by the routine that sorted them. (also note that one thing you can do here is have some object their "left" part mazed first then their right part on even frame, and the other way arround on odd frames for more cycling).

In my current project, I sort the object in function of their vertical position, because the view is top-down, so it's important to have stuff that is on the bottom of the screen mazed with higher priority that stuff that is high on the screen. In the case of a platformer, the order could just be random or anything. In my current project I also treat explosions and this kind of stuff (that I've called myself "Graphic SFX" in my source code) first prior to anything, so they will be above all. If you treat "Graphic SFX" like normal objects (wich my game engine dont, but maybe yours does) then you can have your sorting routine take this into account and made them prior to the exploding object.

by on (#25993)
tokumaru wrote:
Bregalad wrote:
Of course. Then make explosions always have higher priority (that's what I do in my current project) or made the object dissapears at soon as it explodes, being replaced by the explosion instead of being behind the explosion (like in Contra).

Yeah, usually it is done like this... but there are cases when things explode but don't vanish... have you seen what happens to Robotnik after Sonic hits him 8 times?


i figured something as large as Dr. Robotnik's hovering ship would be done like Super C does the 6 legged walker in the jungle level. use a blank name table to render the larger parts that don't move relative the the ship. then just use sprites for the parts that move independent the ship and for the explosion.

the only downside is the background for the boss fight would be blank.