8bitMicroGuy wrote:I was thinking that in my Mario clone game all objects should interact with each other like Goombas colliding with each other or accidentally stomping each other or have one turtle shell roll and touch a moving platform which bounces it off or lifts it up.
That's all possible. The worst that could happen is your game slows down some with like 8 objects on screen. You could do a lot of things to mitigate slowdown, like not running the non important collision for every object every frame. I believe Super Bat Puncher does this for enemy-enemy collisions, and you could do it for something like Goombas bouncing off each other. It's not all that important if they're inside each other for a few frames for that.
Even checking every collision every frame is not necessarily the worst. Just remember that with three objects...
Object 1 checks against object 2.
Object 1 checks against object 3.
Object 2 checks against object 3.
Object 3 checks against nothing. (The other two objects already checked for collision with it)
It's hard to think about that when those should be functions with their own local variables on the stack (C style) and when it's easy to mix it up in ASM due to the lack of CPU registers.
You can use variables on the stack pretty much like C. I'd have to write some tests before I wrote how
, though. I can think of a few ways, but I can't type them blind and be sure they'd work.
How I typically manage is two sets of temp variables. Let's call them temp and ltemp. temps are super volatile. They're free to be changed in every subroutine without regards to others (well almost). For subroutines that call other subroutines, and need things preserved across calls, I use the ltemps. At the point, you do have to check if the function you're calling changes the same ltemp and then use a different one. But it doesn't happen too often. I do it that way mainly for performance, though.
also just use pla/pha for variables across subroutines that call other subroutines. (Which is slightly different than the C style thing I'm talking about above.)
I've decided to not use typedef struct style in this like in the first post (because I learned that from NES's poor indirect indexing explained by ROM City Rampage maker), but arrays of entity members where the entity index in the heap is addressed with X like this
Why not just use something like this?
Code: Select all
objectnum = 20;Max number of objects
spriteramstart = $0300
inst_X_Subp = spriteramstart+objectnum*0
inst_X_Pixl = spriteramstart+objectnum*1
inst_X_Page = spriteramstart+objectnum*2
inst_Y_Subp = spriteramstart+objectnum*3
inst_Y_Pixl = spriteramstart+objectnum*4
inst_Y_Page = spriteramstart+objectnum*5
Code: Select all
; Set position of an entity to zero
; Assumed that CPU register X had been loaded with the entity index in the heap
sta inst_X_Subp, X ; Fraction of X coordinate in subpixels
sta inst_X_Pixl, X ; Integer part of X coordinate in pixels
sta inst_X_Page, X ; High byte of the integer part of X coordinate
sta inst_Y_Subp, X
sta inst_Y_Pixl, X
sta inst_Y_Page, X
X = 0 is the first object
X = 1 is the second object
X = 19 is the last object.
No indexed. This way also allows you to access object info with Y.
Code: Select all
So if you needed to put one object in front of another, you could jsr to inst_PutInFrontOf with the object you want to place in front in X, and the object you want to be behind in Y.
And how you'd write inst_PutInFrontOf depends on what behavior you decide as far as rendering.
If I were you, I'd just make a game and worry about this stuff when it comes up. Trying to plan an engine that covers everything will make you spend time planning for things you may never actually need.
And if a poof and a door are active at the same time, well them's the breaks. There are ways around that, and you'll think of them practically once you've written some code.
My game's entity management is basically what I've described above. I have X object slots, and RAM is reserved for them at compile time. I run through the object list three times (Tokumaru would yell at me
). My player characters are always the first objects processed, and knowing this allows special exceptions. However, the reason I go through the object's list more than once is to cover some special cases (like moving platforms).
In my game objects can be grabbed and carried. If Player 1 (processed first) ran and rendered himself in one step, he'd be in the wrong position because player 2 who grabbed him (processed second) would move the same frame. So... it would become player 2's responsibility to be rerender player 1.
My main philosophy as far as my object management system is that an object almost NEVER affects another object.
So I go through once to resolve all object's positions. I go through again to resolve grabbing. (All object's positions were final, So a grabbed object (or a grabbed grabbed object [or a grabbed grabbed grabbed object]) could have their positions set. I go through a third time to actually render the objects.
In this way, it doesn't matter which object is processed first as far as grabbing. There's never any of that frame behind stuff you often see on moving platforms. It's possible to do what I do in two or one loop through the objects, but I don't for (long winded reason here). One less long winded reasons is that it allows for some hacky stuff. (Some objects do things that aren't related to rendering in their render step. For instance... an object can detect it has been grabbed in its render step, and move itself back to its position before the grab before rendering. This allows the player to trigger a switch with the grab button with no new written behavior.)
Each object in my game has a set of things that it reports to other objects in a single bit.
Stuff like: Object can be bounced off, object can be stomped, object is horizontally collision enabled, object can be grabbed.
And each object has a "mailbox" that allows an object to tell it things. So my player can stomp another object. It will either bounce or not depending on can be bounced off bit. My player will then tell the object it was stomped by setting a bit, rather than say... setting that object's y velocity to 64.
It is the responsibility of an object to update only itself. (There are hacky exceptions, of course!)
So... I have a crate that bursts open when stomped. It checks its "was stomped" bit and destroys itself if set.
I have a ball that's just sent downward without being destroyed. It checks the bit and does that if set.
I have an object planned that will just be toggle ON/OFF if stomped.
The player object has NO idea what of those things the other object is doing, or even if ANYTHING is happening.
As well, enemies can stomp and grab each other. They don't know WHAT stomped them because it pretty much never matters. An object DOES know what's holding it, so that it can set its position properly.
The fact that objects aren't all that aware of each other, means that most of the RAM I've reserved doesn't need to be generic between them. The type bits, position, X/Y velocities and grab bytes are the main ones. Each object also has like... ten bytes it can use for whatever it wants, because the other objects will never touch it. (Even things like animation frame or metasprite don't need to be generic. I can have objects that aren't animated or whatever.) Technically the grab bytes are free to be used for whatever too, as long as the object NEVER reports itself as grabbable. (I uh... use those bytes for another purpose at least once for an object's death animation. But that's hacky.)
The reason my players are always created/processed first (game supports co-op) is so that object order BARELY affects players. For instance... if an enemy was processed in between two players, it might treat those players different. Player 2 (processed second) stomps and kills and enemy in the same frame player 1 (processed first) walks into it. In that case, player 1 gets hurt. Swap the processing order? Player 1 is safe. I do many things to avoid unfairness between players. Even same frame grabs are handled.
Object processing order CAN make a difference with how the enemies act to each other, but the result of the interaction affects each PLAYER the same.
So there are some notes on my Game Entity Management.