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.
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.
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
Maybe I will adjust it maybe not, it is not that important to me.
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.
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
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
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.
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.