It is currently Thu Sep 21, 2017 9:36 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 12 posts ] 
Author Message
PostPosted: Tue Sep 12, 2017 12:57 pm 
Offline

Joined: Mon Aug 14, 2017 12:48 pm
Posts: 2
Hi there. I have a small NES project that supports an animated character who can jump around and collide with background objects. Both the character and the background objects are 16x16 metatiles. To store background data I am just writing #$00s (un-collidable metatile) and #$01s (collidable metatile) directly into PRG-ROM.

Now that I can store background data and collide with it I’d like to move on to storing object data. I’m not sure if “object” is the right term here, but essentially I’m referring to spawn locations for things like enemies or moving powerups. The only object in my project currently is the player, so I’m here to inquire about ways, common or not, to approach this problem.

Thanks so much! :mrgreen:


Top
 Profile  
 
PostPosted: Tue Sep 12, 2017 1:09 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10010
Location: Rio de Janeiro - Brazil
Games normally have a separate list containing all level objects, with information such as X and Y coordinates, type (so you know how to initialize and update each object) and possibly other parameters. In games that don't scroll, all objects of a screen are loaded when the screen is. In games that do scroll, the objects are usually sorted by their coordinates and are loaded as the camera approaches them.

Loading an object means reading its information from the list of level objects in ROM and copying it to an area in RAM dedicated to active objects. Due to memory and CPU imitations, game engines can only handle a few active objects at a time, so the game engine also has to deactivate objects that go too far off screen in order to free memory for new objects.


Top
 Profile  
 
PostPosted: Tue Sep 12, 2017 1:32 pm 
Offline

Joined: Mon Aug 14, 2017 12:48 pm
Posts: 2
tokumaru wrote:
Games normally have a separate list containing all level objects, with information such as X and Y coordinates, type (so you know how to initialize and update each object) and possibly other parameters. In games that don't scroll, all objects of a screen are loaded when the screen is. In games that do scroll, the objects are usually sorted by their coordinates and are loaded as the camera approaches them.

Loading an object means reading its information from the list of level objects in ROM and copying it to an area in RAM dedicated to active objects. Due to memory and CPU imitations, game engines can only handle a few active objects at a time, so the game engine also has to deactivate objects that go too far off screen in order to free memory for new objects.


Got it, thanks. I was hoping the answer was something along those lines.

I have a follow up question to your second paragraph. Do NES devs generally partition parts of their shadow OAM to specific active objects? For example, do they say, “Okay, I will only have X number of goombas on screen at one time, so this part of my shadow OAM will go only towards goomba sprites.” Or do they flesh the system out more to say, “This region of RAM will go towards any active object, and when a new object is introduced it gets the first area of memory available.”


Top
 Profile  
 
PostPosted: Tue Sep 12, 2017 2:29 pm 
Offline
User avatar

Joined: Fri Feb 27, 2009 2:35 pm
Posts: 210
Location: Fort Wayne, Indiana
AndreasSpirakis wrote:
Do NES devs generally partition parts of their shadow OAM to specific active objects? For example, do they say, “Okay, I will only have X number of goombas on screen at one time, so this part of my shadow OAM will go only towards goomba sprites.” Or do they flesh the system out more to say, “This region of RAM will go towards any active object, and when a new object is introduced it gets the first area of memory available.”

I've always just had an index that points at the next available OAM slot, and every frame the index gets reset, the OAM list gets cleared, and every active object that's on-screen adds an entry to the OAM list.

I think the main reason you would partition the OAM list like that would be if you wanted to do layering effects, like you have some sprites that you want to always be above everything else, but that could be solved by just controlling the order things are drawn in too.


Top
 Profile  
 
PostPosted: Tue Sep 12, 2017 2:41 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10010
Location: Rio de Janeiro - Brazil
I believe that SMB actually uses a more hardcoded system where certain areas of RAM are reserved for certain object/enemy types, but it's certainly more versatile to have X slots and let objects grab any free slot. Some may advocate that 16 slots of 16 bytes is enough, and very convenient that it all fits in exactly 256 bytes. I actually use way more than that (24 slots of 32 bytes), but it ultimately depends on the kind of game you're making.

As for hardcoding objects to OAM positions, that's bad no matter the kind of game you're making. If there's any chance that more than 8 sprites may share the same scanlines, you absolutely need sprite cycling, and you can't do that right if objects always use the same OAM positions. Most games allocate OAM slots dynamically.

If you don't use any sort of layering, you can start outputting sprites to a random OAM position and advance a prime number of slots each time. That will completely spread the sprites across the entire OAM space.

If you need to respect sprite priorities within the same object (e.g. an overlaid face like Mega Man's), you can randomize the order in which objects are drawn, and fill the OAM linearly. If you need priority between objects (e.g. a sword the player is carrying), you can link child objects to parent objects so they're drawn together in the proper order.

No matter the approach, efficient sprite cycling requires the entire OAM to be built from scratch every frame, and AFAIK, that's what most games do.


Top
 Profile  
 
PostPosted: Fri Sep 15, 2017 4:57 pm 
Offline
User avatar

Joined: Thu Aug 13, 2015 4:40 pm
Posts: 104
Location: Rio de Janeiro - Brazil
@Tokumaru
Out of curiosity, what are the 16 bytes per object used for, in your games? I'm programming an object manager right now and I thought about 11 bytes being more than enough for my case, but maybe I'm missing something I haven't thought of yet. Here's mine:

byte 1: object ID
byte 2: attributes
byte 3: X coord low
byte 4: Y coord low
byte 5: X coord high (haven't figured out how to do the whole coords thing yet, so I'm reserving these)
byte 6: Y coord high
byte 7: object state
byte 8: current animation frame
byte 9: current animation frame timer
byte 10: horizontal velocity
byte 11: vertical velocity

_________________
http://nesrocks.com/blog/superpitfall30th/


Top
 Profile  
 
PostPosted: Fri Sep 15, 2017 5:15 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10010
Location: Rio de Janeiro - Brazil
I posted my list of object properties a while ago: viewtopic.php?p=171801#p171801


Top
 Profile  
 
PostPosted: Fri Sep 15, 2017 5:27 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 18996
Location: NE Indiana, USA (NTSC)
nesrocks wrote:
byte 1: object ID
byte 2: attributes
byte 3: X coord low
byte 4: Y coord low
byte 5: X coord high (haven't figured out how to do the whole coords thing yet, so I'm reserving these)
byte 6: Y coord high
byte 7: object state
byte 8: current animation frame
byte 9: current animation frame timer
byte 10: horizontal velocity
byte 11: vertical velocity

The Curse of Possum Hollow roughly follows this, assuming "ID" is an actor class, "attributes" are facing direction and palette swap, and "state" is health. Actor class controls which sprite set and which logic routine are used. I often duplicate frames, with two frames pointing to the same metasprite data, to encode state. And sometimes I use the timer for other state-like things and advance the sprite's animation on a multiple of 8 frames.

byte 12: horizontal velocity high
byte 13: vertical velocity high
byte 14: X coord highest (in screens)
byte 15: height of last damage (used for height-dependent damage response)
byte 16: offset into CHR RAM of start of animation frames

There are also about 16 bytes of state reserved for bosses.

EDIT: Here are some of the things tokumaru's engine stores that mine doesn't.

type, bounding box: Determined by logic routine type
next object in same group: Groups are hardcoded to the first or part of the object table, and there are few enough slots that a linear search for type=empty is enough
link to related object: Not used
Y coordinate 3rd byte: Curse scrolls only horizontally
angle: Determined by bit 6 of sprite attribute
slope threshold: all walkers have the same slope behavior
animation script pointer: I use only 1 byte because it's a table index, doubling as a frame number
extended state, general purpose: Only one entity can have extended state, and it's the boss
spawn stuff: Not as necessary, as Curse scrolls only one way


Top
 Profile  
 
PostPosted: Fri Sep 15, 2017 5:43 pm 
Offline
User avatar

Joined: Thu Aug 13, 2015 4:40 pm
Posts: 104
Location: Rio de Janeiro - Brazil
In my case state is current state (walking, idle, jumping) for the class' state machine to handle. I may need to add a health byte.
As for the animation frame and animation timer I forgot I had thought about using the low bits for the timer and the high bits for the animation frame, so 1 byte for both things.

_________________
http://nesrocks.com/blog/superpitfall30th/


Top
 Profile  
 
PostPosted: Fri Sep 15, 2017 5:58 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10010
Location: Rio de Janeiro - Brazil
tepples wrote:
I think tokumaru's objects are bigger because his engine caches a bunch of stuff.

True. The spawn position for example could be found by taking the object's index and looking up its entry in the list of the current level's objects. That's pretty slow though, specially considering that a bank switch is necessary to access the list. It ends up being easier and faster to cache that information when the object is loaded.

The slope thereshold too: I could maybe repeatedly check the metatiles below the object and look at their height maps searching for the highest point, but that'd be crazy slow. Better cache the value from when the object was at the highest point of the slope.

Another thing you don't see very often is angle information, because a lot of games don't have slopes or don't need proper angular physics. Hit boxes also change when objects rotate, so I have a field for that too. A game without rotation could use the state (e.g. standing vs. ducking) to get the appropriate hit box.

Coordinates are usually smaller than mine too, because most NES games don't have Sonic 3-sized levels. I also avoid packing bits in ways that require decoding before I can use them, I'd rather keep everything properly aligned and ready to use, even if that costs a little more space.

I don't see any of you guys keeping track of the instance ID (i.e. index in the list of object definitions), which should be necessary to control spawning (avoiding multiple copies or respawning after death). Are you handling this some other way?


Top
 Profile  
 
PostPosted: Fri Sep 15, 2017 6:22 pm 
Offline
User avatar

Joined: Fri Feb 27, 2009 2:35 pm
Posts: 210
Location: Fort Wayne, Indiana
In Nova the Squirrel, I have the usual 16-bit X and Y position, and 16-bit X and Y velocity, where the four velocity bytes can be reused, usually to hold a position (Roto-disc style enemies store the position they're circling around in the velocity).

Aside from those, I have:
  • Object type, with the least significant bit being the horizontal direction.
  • State (normal, paused, stunned, "active", initializing) but can be a generic byte with some restrictions, like for projectile type.
  • Two bytes that are free to use for any purpose. The first one gets initialized with a nybble from the level data, for things like enemy variants.
  • A generic timer; most objects automatically reset their state when it hits zero.
    tokumaru wrote:
    I don't see any of you guys keeping track of the instance ID (i.e. index in the list of object definitions), which should be necessary to control spawning (avoiding multiple copies or respawning after death).
  • This.

Usually animation frames are just taken directly from a timer or the state. Is it unusual to have the variables' actual meanings so freeform?


Top
 Profile  
 
PostPosted: Fri Sep 15, 2017 6:30 pm 
Offline
User avatar

Joined: Thu Aug 13, 2015 4:40 pm
Posts: 104
Location: Rio de Janeiro - Brazil
In my case I forgot about it (instance ID to prevent double spawning). Tepples doesn't need that byte though, as he mentioned his game only scrolls right.

NovaSquirrel wrote:
Usually animation frames are just taken directly from a timer or the state.

Well, how will I know how long the current animation frame has been displayed? Some animations may have frames that switch faster than others.
edit: humm, so I could just have a full byte for a timer and use it to set a frame. Yeah, that's better, no need to know which frame it is.

_________________
http://nesrocks.com/blog/superpitfall30th/


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 12 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: Bing [Bot] and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group