Object Manager

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Object Manager
by on (#181125)
Hi, I wasn't able to work on my competition game for the past few months but now I'm back, and I still don't know what am I going to do in respect to object management.

What I'm programming right now basically goes like that: there's a list of objects (255 for null) and each object has few bytes for attributes (X, Y, Metasprite, Animation Timer) and some spare bytes for whatever the object's internal logic wants to do. It is defined as follows:

Code:
OBJ_MAX = 16

OBJ_LIST      .ds OBJ_MAX
OBJ_XPOS      .ds OBJ_MAX
OBJ_YPOS      .ds OBJ_MAX
(...)


The Object ID serves as an index to access their respective AI subroutine in the table. Same with Animations, and the Metasprite table.

Code:
;Must load slot # into X first
ObjectLogic_Table:
 .dw _OBJ_Player
 .dw _OBJ_Player2
(...)

Metasprite_Table:
 .dw _MS_Projectile_Default
 .dw _MS_Umbrella_Up
 .dw _MS_Umbrella_Down
 .dw _MS_Chara_Up
(...)


The gist of it is that the update loop runs through the whole first row of the table, scanning for valid objects, then jumps to its respective logic subroutine (using the pointers from the Logic table). The "AI" subroutine then does whatever it has to do internally (update timers, update metasprite, move, collide, etc.). I was wondering two things:

1. How do you handle collisions in this case? Actually run a collision detection routine in a separate function to set a flag in the object's internal variables, so it can decide by itself?
2. Is this whole indirection thing too wasteful? Are there better ways to do it without having to copy pointers to ZP to do indirect access?

Everything I wrote ultimately boils down to:
tl;dr: How do you guys do object management in your games?
Re: Object Manager
by on (#181126)
I do it pretty much the same way, with the same amount of indirection. This is not too much indirection IMO, just enough to keep things organized and dynamic.

Regarding collisions, I feel like the best thing is to have each object test for collisions between itself and whatever other objects are relevant. Testing for collisions outside of the AI routines sounds wasteful to me, because not all objects can collide with each other, so I find it better to let each object decide if it should collide with anything (a decision that might even be affected its state). There's also the fact that you have to test for collisions AFTER moving the objects, and if you do it outside of the AI routines you'll need a second iteration over the objects so they can react to the collisions.

What I do is separate the objects in groups that are processed in a constant order, and objects that affect others go in the first groups, while objects that are affected by others go in the later groups. This means that after an object is processed, its status is final, but future objects can react to collisions with it. This requires careful ordering of the object types so all interactions work as intended. Some reactions might have a 1-frame delay, or specific object types may exceptionally be visited twice if really necessary.
Re: Object Manager
by on (#181127)
What I'm doing is basically coding each object type's update routine (the "AI") separately, keeping very little stuff generic, then whenever I need another object to do the same thing I pull that logic out into a subroutine, optimizing as I go along rather than trying to plan everything ahead. Ideally I want each object's individual routine to be as short as possible, mainly calling a bunch of other generic subroutines, but I don't start out that way. The more flexible you want an objects features to be, the more performance overhead you'll waste. Some times its better to have multiple versions of the "same" routines depending on the complexity you need.
This way I also only ever check for collisions on objects where it's actually relevant, and only in situations where it's relevant.
Re: Object Manager
by on (#181133)
I'm still trying to come up with a better way to handle collision responses. I guess that revisiting only the objects that have been involved in collisions wouldn't cause much of an overhead, so updating them all, then testing for all possible collisions, and finally letting the objects handle the collision that have happened seems like an interesting solution. The most complex problem I see with this is if a specific collision response, such as being pushed away by an object, generates another collision... This could go on forever, like when the player is crushed by two walls closing in on it. both walls would keep ejecting the player into the opposite wall in an infinite loop, unless you did something to detect this specific situation.

You'd also need a way to specify which collisions should be tested for each object, to prevent unnecessary collision tests. Maybe you could have different groups for enemies, items, projectiles, and so on, and each object would have a bit field indicating which groups it can collide with. Another interesting thing to do would be standardize the collision responses, so that objects wouldn't need to probe other objects' IDs in order to decide how to react. It would be better if the collision routine could send messages like "you've been pushed away from the right by this solid obstacle", and the response code could simply check the obstacle's hitbox to adjust the object's position accordingly. Other messages could be "you've been hit from below by this enemy", or "you've touched this item".

I really appreciate when we can keep things more generic and dynamic, instead of having to check attributes that could change in the future, which could end up breaking things you didn't anticipate.
Re: Object Manager
by on (#181144)
It really depends on what type of game you're making. Most typical NES style games don't require a lot of "inter-object collisions", and I doubt any licensed NES game ever checked follow-up collisions on the same frame, it's simply not worth it - we're making games, not physics simulations. A lot of them don't even check collisions on every frame.
I think it's a very good idea to handle specific object types seperately if it fits into your game design. For a game with a lot of stateless, straight moving projectiles, you probably don't need a lot of the extra data you'd need in your object table for other kinds of objects.
Re: Object Manager
by on (#181159)
Collision seems to be a complicated subject, especially on the NES. My game ideally will have "projectiles" that bounce through some background objects, destroys others, destroys itself on impact with a specific type of background tile, etc. while also bouncing off player's pads. My approach now is to run the collision detection in the projectile object to check against the background but I'm not sure about checking the player in that internal routine too... I decided not to have cross projectile collision checks as it would overly complicate the program, which is bad since I want to meet the competition's deadline (and actually finish the game). I too wish there was a more generic way of doing collisions without straining resources too much, my approach (our?) feels too hackish in respect to even the simpler 2D physics engine available on modern platforms.

You two are doing conceptually exactly what I'm doing for object management, I think. That's a relief, thought there were obvious alternatives with clever code for that.

EDIT: Done punching in the object management code. Works pretty well! I'm just worried about the number of instructions required to process everything, right now the behavior of the objects are very simple. I'm expecting to have that CPU line thingy to be past half the screen with AI enemy objects and collision detection.

Now I just need to finish subroutines for AABB collision detection and animations, the latter almost ready, then I'm set to focus on object behavior (and the game itself).

Image