How to handle entities that must survive 1 complete frame

So it’s generally best practice to leave entity destruction up to a single system (usually the decay). This is good because you can easily have systems that last for a specific amount of time for basically free (fire dealing damage over time is a good example). The problem I am currently running into is systems that must work for exactly 1 frame. Before I took for granted that I could destroy these entities in their respective systems but that eliminates the ability to easily add things like the above damage over time example.

My problem is when a projectile impacts a collider with a health component, the projectile will create a new entity with an attack component, and a decay component with a lifetime of 0.

My decay state simply detects when new decays come into existance, adds them to a map with their id and their expected lifetime, and subtracts the current tpf from their lifetime every frame. Once their life is <= 0 they are destroyed.

Because my decay state is so universal I typically add it first, but this causes a problem: my lifetime 0 entities are killed at the very beginning of the frame, before other systems have a chance to see them. If I carefully lay out the order of my systems I can sort of work around this, but now system order is extremely important, and I am starting to lose the benefits of the ecs. If I delay the decay from acting for 1 frame I still need to very carefully lay out the system order or some systems may act on the same entity twice.

What’s the best practice for implementing systems that need to work exactly once? Should I ignore the ecs for these systems? Should the attack system be evolved to track a total amount of damage per attack, and a max damage per frame to ensure the entire attack is accounted for? The last one sounds appealing but it also means I won’t be using the simpler decay method for persitant damage.

Can’t you just consume the component and remove it? So skipping the decay system. I do something similar with physical entities and impulses. When an impulse component is added to my physical entity, I retrieve the component, apply the force and just remove it.

That is how I used to do it alot, but by consuming the component you lose the ability to “build up” complex behaviour. IE if I have a napalm bomb vs a dumb bomb. Both can use an explosion radius to inflict an attack, but the dumb bomb would inflict an attack of 10, decay 0 while the napalm could inflict an attack of 1, decay 10.

As of now I am moving forward with the concept that system execution order is important and I am making sure to respect it.

Sometimes system execution order is important.

But also sometimes “this must be destroyed exactly at the end of this frame” is not important.

For example, you could setup your decay system to never remove the components in the same frame it discovers them… so they would always at least last from one DecayState update to the next. Basically, loop through all existing decay first then update for new decay.

I’m not saying it’s the answer… but a way to remove system ordering issues is have them each only consider their own frame to frame cycle like that. Each system only deals with the changes since its last update.

Still, I never get around some kind of system ordering in the end but most of the time it shouldn’t matter.

Edit: P.S.: is there any reason that you aren’t using the standard decay stuff from SiO2? That may also shed some light in your custom needs in this case.

2 Likes

Yeah, I guess some things will always be bound to order. As for not using sio2 standard stuff, it’s cause I don’t use sio2. I’ve never really looked into it tbh, I just use zay-es and lemur and usually only 1 or 2 other libs for any project and I’ve written everything else myself. I taught myself all the java I know through jme so I approach most roadblocks as learning experiences, but recently I’ve been approaching my projects with a stronger emphasis on finishing them. I’ve started to look into these little issues more deeply to ensure I understand the concepts.

1 Like

Makes sense.

For Lemur and Zay-ES users, SiO2 is just a nice pack of utilities for building a game. (For anyone, I guess… but mostly Zay-ES users with some Lemur helper classes also.)

Even if you don’t use it, maybe there are some ideas in there: GitHub - Simsilica/SiO2: Base pack of useful reusable game code that can help bootstrap any JME game project.

1 Like