--
By saying Your systems should be getting components in packs (grouped by entity) from common pool (instead of creating it each for it self) I meant to say there is no composition relation between systems and components. Your systems should not have components directly attached to it.
Here is minimal example of ECS with the position, velocity and 'rendering' components. It is not optimized, lacks encapsulation, manager misses few methods (at the very least deleteEntity), but I hope it captures the spirit in least lines of code possible.
public class Component {
public final static int POS = 1 << 0,
VEL = 1 << 1,
RENDER = 1 << 2;
public static class Velocity {
float velx, vely;
}
public static class Position {
float x, y;
}
public static class Rendering {
String name; // for sake of having at least one field inside.
}
}
--
public class Systems {
public static class Velocity {
public void update(EntityManager manager) {
int required_components = Component.POS | Component.VEL;
for (int i = 0; i < manager.size; i++) {
if ((manager.flag[i] & required_components) == required_components){
manager.pos[i].x += manager.vel[i].velx;
manager.pos[i].y += manager.vel[i].vely;
}
}
}
}
public static class Render {
public void update(EntityManager manager) {
int required_components = Component.POS | Component.RENDER;
for (int i = 0; i < manager.size; i++) {
if ((manager.flag[i] & required_components) == required_components) {
System.out.println(String.format("%s: (%f x, %f y)", manager.rendering[i].name, manager.pos[i].y, manager.pos[i].y));
}
}
}
}
}
--
public class EntityManager {
public int flag[];
public Component.Position pos[];
public Component.Velocity vel[];
public Component.Rendering rendering[];
public final int size;
public EntityManager(int size) {
this.size = size;
pos = new Component.Position[size];
vel = new Component.Velocity[size];
rendering = new Component.Rendering[size];
flag = new int[size];
}
public int createEntity(int flag) {
for (int i = 0; i < size; i++){
if (this.flag[i] == 0) {
this.flag[i] = flag;
if ((flag & Component.POS) > 0) pos[i] = new Component.Position();
if ((flag & Component.VEL) > 0) vel[i] = new Component.Velocity();
if ((flag & Component.RENDER) > 0) rendering[i] = new Component.Rendering();
return i;
}
}
return -1;
}
}
--
public class GameContainer {
public static void main(String[] args) {
new GameContainer();
}
EntityManager manager;
Systems.Render renderingSystem;
Systems.Velocity velocitySystem;
GameContainer() {
manager = new EntityManager(5);
renderingSystem = new Systems.Render();
velocitySystem = new Systems.Velocity();
int id = manager.createEntity(Component.POS | Component.VEL | Component.RENDER);
if (id > -1) {
manager.pos[id].x = 10;
manager.pos[id].y = 10;
manager.vel[id].velx = 1;
manager.vel[id].vely = 1;
manager.rendering[id].name = "player";
}
id = manager.createEntity(Component.POS | Component.RENDER);
if (id > -1) {
manager.pos[id].x = 0;
manager.pos[id].y = 0;
manager.rendering[id].name = "tree";
}
startGameLoop();
}
void startGameLoop() {
while(true) {
velocitySystem.update(manager);
renderingSystem.update(manager);
}
}
}