ECS Pattern: Decay

There are so many things in game which have some kind of a life time. A bullet, an effect, vanishing tiles in a can't stop running game, dying enemies and many more things which have a life time.

In the past I would have done it seperately depending in which situation something hast to vanish. With the ECS you can solve all of them with one component and one system.

Component

class Decay implements EntityComponent {
    private final long start;
    private final long delta;

    public Decay(long deltaMillis) {
        this.start = System.nanoTime();
        this.delta = deltaMillis * 1000000;
    }

    public double getPercent() {
        long time = System.nanoTime();
        return (double)(time - start)/delta;
    }

    @Override
    public String toString() {
        return "Decay[" + (delta/1000000.0) + " ms]";
    }
}

Depending on what delta you handed over to the constructor getPercent will increase slowely or quickly to wards 1, representing the progress of the decay.

System

To handle all the entities with a decay you need a system.

package ch.artificials.invaders;

import ch.artificials.invaders.component.Decay;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.simsilica.es.EntityData;
import com.simsilica.es.EntitySet;

public class DecayAppState extends AbstractAppState {

    private SimpleApplication app;
    private EntityData ed;
    private EntitySet entities;

    public DecayAppState() {
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        this.app = (SimpleApplication) app;
        this.ed = this.app.getStateManager().getState(EntityDataState.class).getEntityData();
        this.entities = this.ed.getEntities(Decay.class);
    }

    @Override
    public void cleanup() {
        this.entities.release();
        this.entities = null;
    }

    @Override
    public void update(float tpf) {
        entities.applyChanges();
        entities.stream().forEach((e) -> {
            Decay decay = e.get(Decay.class);
            if (decay.getPercent() >= 1.0) {
                ed.removeEntity(e.getId());
            }
        });
    }
}

In the update loop we go through all entities with a Decay and check if the decay is bigger or equal 1 to remove the entity. After this point in your game you have a time to life for every entity with a Decay component, you don't need to care any more about destroing this entity. Also getPercent could be displayed in a progress bar. Also you can easily update a timeout by just set a new Decay component to your entity which will overwrite the existing one.

Side Note

The decay system was the point of no return for me to use ECS, this thing here convinced me that there is no better and no easier way than a ECS to hack a game.

Comments

comments powered by Disqus