Page 2 of 2

Posted: Wed Sep 07, 2011 9:09 pm
by Celius
Dwedit wrote:I'd just use a object map ID number to relate an object (in your active object table) to a numbered enemy (in your object map). An object ID of 255 would indicate a temporary object, such as a bullet.
No need for a separate table, or special memory format. You could even make temporary enemies that disappear when they move too far away.
If I'm understanding correctly, you are saying to treat spontaneous objects kind of the same way as intelligent objects (so just one format), but assign spontaneous objects a reserved ID that indicates they are spontaneous. Is that correct (somewhat)?

With the system I use, my main concern would be RAM usage. I don't know if people do it really differently, but I have implemented a system of slots for these intelligent and spontaneous objects. Each intelligent object is given 22 bytes of RAM, and each spontaneous object is only given about 7.

If I were to treat intelligent objects and spontaneous ones the same way, I would be wasting a lot of RAM for things like bullets (all I need is an X coord, Y coord, and maybe 1-2 bytes of RAM to work with for special movement. Oh, and an ID). The other concern is that since objects would all be using the same slots, spontaneous objects could accumulate in the active zone and prevent intelligent objects from spawning. This is very bad. If anything should be prevented from spawning, it should be a spontaneous object, not an intelligent one.

Posted: Wed Sep 07, 2011 9:42 pm
by tokumaru
I treat all my objects the same. They have a "setup" routine, where they install themselves to one of the free object slots in RAM, using parameters (X and Y position, etc) from the stack. These objects can either come from the list of objects in ROM (similar to your intelligent objects) or be created by another object (spontaneous), from the point of view of the object it doesn't matter, since they just grab the parameters from the stack.

The main difference is that spontaneous objects have invalid pointers to the table of states in RAM, so they don't write anything back when they die. Simple objects will indeed underuse the RAM that's allocated to them, but I don't mind having this in exchange of generalization. I have 24 active object slots, things must be pretty busy for all of them to be used... I don't expect that to happen very often.

Posted: Wed Sep 07, 2011 11:26 pm
by Celius
I see how it would work, and I guess it makes a difference also if your enemies don't throw a lot of projectile weapons. In my game, things shoot fireballs, rocks, drool lava out of their mouths, etc. So there are a lot of things flying around.

And with my set up, I've dedicated a lot of RAM to other things (animation slots, a sprite drawing stack, the OAM page, a music engine, a two-page copy of the screen for fast collision detection, etc.). Needless to say, I have very little left to work with after coding all that, so I would be in trouble if I decided to make everything universal.

Just curious, how many bytes do you allocate to an object? At first I feel like 22 bytes is a lot for a simple game like mine, but then things like object IDs, coordinates with precision, status bits, velocity, etc. all start adding up.

Posted: Thu Sep 08, 2011 6:08 am
by tepples
Celius wrote:
tepples wrote:
Celius wrote:Actually, another thing I implemented was a "freeze" zone. An object is able to move around most of the activity zone, but towards the outer edges, objects will stop moving.
Sort of like how Super Bat Puncher remembers the position of every enemy on the screen, but as soon as they leave the screen, they freeze and their AI resets.
I'm not sure I understand. What happens when the AI is "reset"?
It means the enemy's AI goes into the "sleep" state, the same state the enemy was in before you encountered it. This frees up CPU time, and it collapses the enemy to just its index in the map's enemy list, its coarse X position, and its coarse Y position. I don't know exactly how Super Bat Puncher works, but some engine architectures have a limited number of simultaneously active "intelligent" objects, and putting an enemy to sleep might free up such a slot.

Posted: Thu Sep 08, 2011 11:56 am
by tokumaru
Celius wrote:I've dedicated a lot of RAM to other things
I see... I too have used nearly every byte of the stock 2KB of RAM, I have only a few ZP bytes left.
Just curious, how many bytes do you allocate to an object? At first I feel like 22 bytes is a lot for a simple game like mine, but then things like object IDs, coordinates with precision, status bits, velocity, etc. all start adding up.
I have 28 bytes per object, with 24 of them the total is 672 bytes for the active objects. That amount seems OK for most objects, but I imagine that some really complex objects might need more than 1 slot. In that case the additional slots don't have any A.I., they are only used as extra memory for the master object.

Posted: Thu Sep 08, 2011 1:20 pm
by Bregalad
I see... I too have used nearly every byte of the stock 2KB of RAM, I have only a few ZP bytes left.
Well it's fun because I only use half of both main RAM and zero page currently in my game engine.

Note that to save memory, you might not allow TOO much objects to be loaded at a time on the screen (I personally choose 8 slots), and also you should recycle variables if their utility is mutually exclusive. You say you have 24 slots, I think it's way too much. Even if you load all 24 slots with something chances are that your engine will lag terribly.

For example, in a projectile you don't need any variable to remember it's hit points, so you can use this variable to store the speed of the projectile instead (this is just an example).

That way I was able to fit all my object-related variables in arround 200 bytes.

Posted: Thu Sep 08, 2011 1:34 pm
by tepples
Bregalad wrote:For example, in a projectile you don't need any variable to remember it's hit points
Unless different projectiles using the same sprite have different damage amounts, or unless a projectile is programmed to disappear after a certain distance. Where there's memory, game designs will expand to fill it.

Posted: Thu Sep 08, 2011 1:45 pm
by tokumaru
Bregalad wrote:Note that to save memory, you might not allow TOO much objects to be loaded at a time on the screen (I personally choose 8 slots)
With multi-directional scrolling you have to be a bit more generous... Objects can appear from all sides, and preferably a while before they scroll in, so you need at least twice what you'd need in a static-screen game.

I'm trying to mimic the way Sonic games work, and if I'm not mistaken they have 96 object slots. Obviously it's not common to have that many active objects, but if you use debug mode to manually place that many objects the game becomes insanely slow.

I also don't plan on having 24 active objects all the time, but I want to avoid the disastrous situation that would be to refuse a slot to an important object, such as the level's boss.
and also you should recycle variables if their utility is mutually exclusive.
Oh, I do that a lot.
You say you have 24 slots, I think it's way too much. Even if you load all 24 slots with something chances are that your engine will lag terribly.
Sure it will lag, but I really don't plan on having that many objects very often, I just want to be prepared for slightly busier areas. In a free scrolling games enemies can walk to areas they wouldn't ordinarily go to, depending on how the game is played, so even if you design the areas to not have too many objects grouped together they might still move around and gather into larger groups. I just want to be prepared.

Also, since some objects might use more than 1 slot, it's a good idea to have some room to breath.
For example, in a projectile you don't need any variable to remember it's hit points, so you can use this variable to store the speed of the projectile instead (this is just an example).
Oh yeah, I do that too.

Posted: Thu Sep 08, 2011 5:14 pm
by tepples
tokumaru wrote:I also don't plan on having 24 active objects all the time, but I want to avoid the disastrous situation that would be to refuse a slot to an important object, such as the level's boss.
Some games handle this by evicting a relatively unimportant object. Kung Fu even has an animation for when an enemy gets evicted before the boss battle: it walks toward the trailing edge of the screen. So does Super Mario Bros. 3: when Mario grabs the card, enemies turn into coins.

Posted: Thu Sep 08, 2011 6:09 pm
by tokumaru
tepples wrote:Some games handle this by evicting a relatively unimportant object.
Yes, this is something I want to implement too. I'll probably make their importance relate to their codes, to make it easy for important objects to identify objects that can be evicted.

Posted: Thu Sep 08, 2011 9:23 pm
by thefox
tokumaru wrote:
Celius wrote:I've dedicated a lot of RAM to other things
I see... I too have used nearly every byte of the stock 2KB of RAM, I have only a few ZP bytes left.
Did you count a sound engine in to the RAM budget yet? :)

Posted: Thu Sep 08, 2011 9:35 pm
by tokumaru
thefox wrote:Did you count a sound engine in to the RAM budget yet? :)
Heh, good question! Indeed, I don't have a sound engine yet, but I reserved around 200 bytes for one. No matter what happens I'll have to make an engine that uses that many bytes or less. I don't think this is unrealistic... do you? =)

Posted: Thu Sep 08, 2011 9:57 pm
by thefox
tokumaru wrote:
thefox wrote:Did you count a sound engine in to the RAM budget yet? :)
Heh, good question! Indeed, I don't have a sound engine yet, but I reserved around 200 bytes for one. No matter what happens I'll have to make an engine that uses that many bytes or less. I don't think this is unrealistic... do you? =)
Yeah that should be enough, I think FamiTone uses a bit less than that. My sound engine uses ~300 bytes currently I think, but it also has a lot more features and some room for optimization. Hopefully I can get it under 256 bytes...

Posted: Thu Sep 08, 2011 10:25 pm
by Celius
I have a very simple sound engine I'm currently using. It only uses about 112 bytes (though I think I'll have to use about 16 more due to a complication when switching between sound effects). The sound engine doesn't support pitch bending, which is a feature I omitted to save RAM and ROM consumption. So 200 bytes should definitely work.

And there are many workarounds for unsupported features. An echo, for example, you can do with a volume envelope (your engine should support volume envelopes for the square waves). Or in a lot of cases, you can get away with using volume fluctuation or trilling between two notes instead of having vibrato done with a pitch bend. Though I'll say it really doesn't sound the same. If you listen to FF1, CV1, or SMB1, you can really tell the pitch bend is missing.
tepples wrote:
tokumaru wrote:I also don't plan on having 24 active objects all the time, but I want to avoid the disastrous situation that would be to refuse a slot to an important object, such as the level's boss.
Some games handle this by evicting a relatively unimportant object. Kung Fu even has an animation for when an enemy gets evicted before the boss battle: it walks toward the trailing edge of the screen. So does Super Mario Bros. 3: when Mario grabs the card, enemies turn into coins.
This is one of the main reasons I separated spontaneous and intelligent objects. A spontaneous object cannot appear and disallow an intelligent object from appearing. I've also included a unique master routine for every level in my game. This routine has the authority to override anything done with the main engine. So I can technically say "when you get to this point in the level, destroy all enemies and make room for the boss" in that routine. Actually, this routine is the one that handles events of any kind.
tepples wrote:It means the enemy's AI goes into the "sleep" state, the same state the enemy was in before you encountered it. This frees up CPU time, and it collapses the enemy to just its index in the map's enemy list, its coarse X position, and its coarse Y position. I don't know exactly how Super Bat Puncher works, but some engine architectures have a limited number of simultaneously active "intelligent" objects, and putting an enemy to sleep might free up such a slot.
That sounds like deactivating the enemy...? :?

With what I do, I just prevent the enemy from acting until it gets out of the freeze zone. Say you have an enemy that paces back and forth. If you move so that it just spawns, walks towards you, and walks away (past its spawn point), it will essentially disappear until you walk back, and then reveal its spawn point again. My solution was to stop the enemy if it's in the freeze zone, which is next to the inactive zone. This does mean that enemies are only deactivated by the player killing them, or moving so that the frozen enemy is pushed into the inactive zone.

Probably a better solution is the one where you check if the spawn point is in range, and only deactivate them if the spawn point is not. But for certain reasons like collision detection, I can't have enemies acting outside of the activity zone. My two-screen copy of the screen allows me to really quickly search for the tile being stood on (The formula is (XCoord AND $01F0) + (YCoord / 16) + $0600). Thus, if enemies act outside of a 2 screen zone, they will be colliding with garbage, which could be disasterous.

Posted: Fri Sep 09, 2011 4:07 am
by tepples
See also previous discussion on sound engine RAM use.
Celius wrote:
tepples wrote:It means the enemy's AI goes into the "sleep" state, the same state the enemy was in before you encountered it.
That sounds like deactivating the enemy...? :?
There's a difference between deactivating the enemy in place, which is what SBP does, and deactivating the enemy and sending it back to its spawn point, which most NES platformers appear to do.