Zay-ES getEntities question

Hello I am using a setup like this:

public abstract class ActionControl implements EntityComponent

public class CharacterActionControl extends ActionControl 

And I’m trying to do:

entityData.getEntities(ActionControl.class);

But it will not get CharacterActionControl from entity list. Only if I use:

entityData.getEntities(CharacterActionControl.class);

How would I go about selecting entities with components that implement ActionControl?

Using inheritance in this way is not supported as it’s a sign that you are using entity systems incorrectly.

Can you explain what you are really trying to do because it seems like your design may be off track somewhere.

I am new to ECS. trying to get my head around it. I want to be able to add custom action controllers to any entity. Without having to create new appstates everytime. Heres my code.

The appstate:

public class ActionControlAppState extends BaseAppState {

    private EntityData ed;
    private EntitySet controlledList;
    private InputManager input;

    @Override
    protected void initialize(Application app) {
        ed = app.getStateManager().getState(EntityDataState.class).getEntityData();
        controlledList = ed.getEntities(ActionControl.class);

        input = app.getInputManager();
    }

    @Override
    public void update(float tpf) {
        if (controlledList.applyChanges()) {
            removeEntities(controlledList.getRemovedEntities());
            addEntities(controlledList.getAddedEntities());
//            updateEntities(controlledList);
        }
        for (Entity e : controlledList) {
            ActionControl c = e.get(ActionControl.class);
            c.update(tpf);
        }
    }

    private void addEntities(Set<Entity> addedEntities) {
        for (Entity e : addedEntities) {
            e.get(ActionControl.class).init(input);
        }
    }

    private void removeEntities(Set<Entity> removedEntities) {
        for (Entity e : removedEntities) {
            input.removeListener(e.get(ActionControl.class));
        }
    }

    @Override
    protected void cleanup(Application app) {
    }

    @Override
    protected void onEnable() {
    }

    @Override
    protected void onDisable() {
    }
}

my Action control:

public abstract class ActionControl implements EntityComponent, ActionListener {

    public abstract void init(InputManager input);

    public abstract void update(float tpf);
}

Character action control

public class CharacterActionControl extends ActionControl {

    private boolean left, right, up, down, jump;
    private Vector3f walkDirection = new Vector3f();
    private SideScrollCharacterControl physicsControl;

    public CharacterActionControl(SideScrollCharacterControl physicsControl) {
        this.physicsControl = physicsControl;
    }

    @Override
    public void init(InputManager input) {
        input.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
        input.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
        input.addMapping("CharJump", new KeyTrigger(KeyInput.KEY_SPACE));
        input.addListener(this, "CharLeft", "CharRight", "CharJump");
    }

    @Override
    public void onAction(String name, boolean isPressed, float tpf) {
        switch (name) {
            case "CharLeft":
                left = isPressed;
                break;
            case "CharRight":
                right = isPressed;
                break;
            case "CharJump":
                jump = isPressed;
                break;
        }
    }

    @Override
    public void update(float tpf) {
        walkDirection.set(0, 0, 0);
        if (left) {
            walkDirection.addLocal(Vector3f.UNIT_X).multLocal(-1);
        }
        if (right) {
            walkDirection.addLocal(Vector3f.UNIT_X);
        }
        physicsControl.setWalkDirection(walkDirection.multLocal(5));

        if (jump) {
            physicsControl.jump();
            jump = false;
        }
    }

}

Well, already your component is actually ‘doing something’ which means this is no longer an ES really.

Components are only data. Just data, nothing else. They don’t do work.

Also, this statement…

Automatically implies that if there is only one app state then there is only one set of components. No need to subclass anything because the app state will be doing its job with the data provided.

You use the word ‘control’ a lot. Note: JME style controls are 100% NOT components in an entity system. Thinking that way is wrong on a very fundamental level.

Interesting. Okay. So would I do something like this:

MovementControlAppState with ed.getEntities(ActionComponent.class)

ActionComponent(Spatial) and the MovementControlAppState does work on spatial from the ActionComponent?

There will only be one component using the MovementControlAppState. Feels weird making a appstate for that. Or is this how its meant to be?

Hi

To get you head around the ES stuff a sample you can study helps, really. I wrote a blog article about a sample and ported that to the Zay ES wiki into the “Case Studies” section. I guess it could help to see the principles in action.

In very short:
Entity is just an id
Components are only data so you have only getters there
Systems care about the logic and what happens with the entities.

Yeah I’ve read your series. It helped me get started. I still have a lot to understand about this though.

I don’t understand your ControlAppState. You have an EntitySet for the player ship. Does the player control multiple ships?

And when you update:

ship.stream().findFirst().ifPresent(e -> {
    e.set(new Position(position));
});

That looks so inefficient to me. Wouldn’t it be best to store the entity itself versus the EntitySet, and do this?

ship.set(new Position(position));

I’m just trying to understand. Forgive me if I sound ignorant.

The newer Zay ES hast some kind of WatchEntity object to deal better with that, on the writting of my space invader there was none such thing, so I had to do it that way. I could control more ships (could be an upgrade) yes :smile:

Maybe I will adjust it maybe not, it is not that important to me.

Ahh WatchedEntity. Thanks. I’m going to give that a look.

So pretty much if I want new functionality from a set of components I have to make a new appstate?

yes that’s the idea and that’s the beauty IMO as it is completely independent and can be added/removed at any time without bothering much. If you have a set of systems you will be surprised that it can be used for different stuff, see the decay system. And you can by adding removing components to an entity switch on/off behaviours. Need a health bar for trees too? Just add health component to tree entities and you have it (if done already for players, enemies of course), just as an example.

Hmm yeah. I’m starting to get it. But I cannot figure out how to properly use WatchedEntity.

Is there a reference to how? I skimmed the source code

When you create your player entity in your let’s say GameAppState make a getter for the player entity id. Then when you need it, just get the player entity id via

app.getStateManager.getState(GameAppState.class).getPlayerEntityId()

in your wished app state and then “watch” this entity id. Not sure if that is 100% the correct way but I do not see any other to be honest.

Yeah that’s how I was going about it. Now my main issue is how do I know if the WatchedEntity was removed? SO that I can remove it from the InputManager

Also: Should I save the WatchedEntity in GameAppState so I don’t have to create a new one for every appstate that watches the same entity? Or should I Create a new watched entity from each appstate that uses it?

Yeah I asked my self the same question, as my original invader game (not the one in the blog) did had 3 ships (3 lives), but only one of them “active”. I guess the idea is that a watched entity never disappear from the game and you hold a “state” component instead.
But here I’m pretty unsure maybe @pspeed can shed some light? And honestly that was the reason so far, that I did not change the sample to use WatchedEntity :smile:

My WatchedEntity has no components. I used the same EntityId used to setComponents… weird.

Your player need a position and a model, like all the other stuff you want on to see. Else it is a little bit pointless IMO?! Your control system do change that position depending with how much speed you travel when you go forward, backward, whatever. The visual system will draw depending on the new position.

So the system change components of entities

ed.setComponent(yourPlayerEntityId, new Position(cur.x + speed.x *tpf, cur.y + speed.y * tpf). 

EDIT: Uhu, because it is a watched entity it do not really need components. Ok. but the other systems want to display your player need!

No I mean there are components. But when I do:

WatchedEntity player = ed.watchEntity(playerId);

player.getComponents().length <- this is zero.

Even though I’ve added components to using setComponents(playerId, comp…);

Should be that I guess:

ed.watchEntity(playerId, Position.class, Model.class);

And with ed.getComponent(playerId, AnyOther.class) you also can get components which you do not watch.

1 Like

Ahh wow. okay attempt 2. Yeah that was it. And I had to watch the entity after I setComponents

Nah… but you do have to update it with applyChanges() periodically. You can watch an entity any time. It’s one of the differences between it and EntitySets.

From one of your earlier posts… ActionComponent(Spatial). Spatials are visual things… they don’t belong in the ES. In something like Model-View-Controller (MVC), Spatials are the view and the ES is the model.