Entity Component System Part 1

Cool, bright and beautiful

Entity Component System short ECS or even shorter ES is one of the coolest, brightest and most beautiful paradigm in programming I have ever met. It is data driven and completly different to object oriented programming most people know nowadays. The entity system approach brought me back the feeling of the old days hacking games on a ZX81 with a 4KByte RAM extension. Really.

There are many good articles about entity systems, therefor I will not waste my time to write yet another one, just google and you get enough information. I'd like to write a series of blog articles to show you the beauty of the entity system by programming an example game with it. I will show you some of the design patterns I know with that example game.

The game I have in mind is a pretty simple invader like game with the famous jMonkeEngine and Zay ES. I'm currently not sure how far I will go and to how many parts this article will grow.

I will try to show all code in this series, so you you just can copy paste it and try out. Like in the old days where you did buy magazins and type those hex number rows in your comodore 64 and see what kind a game will resulting from it - or none at all because you made a typo somewhere in those 3 pages of hex digits.

Use a monkey

First you have to download JME 3.1 and make it work, it is pretty straight forward. Further more download as well Zay ES and its dependencies guava and slf4j-api. After that start a new game project in JME with the following steps: "New Project -> JME3 -> BasicGame"

I did simply add the jars with: Right click your project "Properties -> Libraries -> Add JAR/Folder". You can have them where every you want, I personally have them in a lib folder in my JME3 project.

Hint: Maybe you want to play through the Examples of JME3 first. I guess it is helpfull to understand the basics. But it is not really mandatory. Up to you, I don't care.

So now we are prepared for the next steps.

The boring part

Let's start with the main part...

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.renderer.RenderManager;

public class Main extends SimpleApplication {

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    public Main() {
    }

    @Override
    public void simpleInitApp() {
    }

    @Override
    public void simpleUpdate(float tpf) {
    }

    @Override
    public void simpleRender(RenderManager rm) {
    }
}

Hit F6 and start your game. After selecting the resolution you only see a black screen. Not very chilling indeed.

The beef

It is all about the the entity system so lets setup and initialize Zay ES before we head into the game itself.

package mygame;

import com.jme3.app.state.AbstractAppState;
import com.simsilica.es.EntityData;
import com.simsilica.es.base.DefaultEntityData;

public class EntityDataState extends AbstractAppState {
    private EntityData entityData;

    public EntityDataState() {
        this(new DefaultEntityData());
    }

    public EntityDataState( EntityData ed ) {
        this.entityData = ed;
    }

    public EntityData getEntityData() {
        return entityData;
    }

    @Override
    public void cleanup() {
        entityData.close();
        entityData = null; // cannot be reused

    }
}

There is not much to say about this application state. As it is brain dead simple.

Black screen? I mean... hello?!

Indeed a game without visual representation is not much fun. Furthermore it always helps to see something on the screen and gives feedback to keep us happy and motivated.

A star ship

We need models which we could do with blender and import it into JME3.

First we need a space ship, so fire up your blender and do that. Well, I do not want to waste your time so I have one on my google drive to share with you. Doesn't mean you can not come up with our own model though.

If you have your model, place the blend file in the assets "Models" folder, then just right click your blender model in JME3 and select "Convert to j3o Binary".

Model factory

After you took that hurdle with blender a factory to get the spatial in JME3 would be greate, it makes things simpler.

package mygame;

import com.jme3.asset.AssetManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
 
public class ModelFactory {
    private final AssetManager assetManager;
 
    public ModelFactory(AssetManager assetManager) {
        this.assetManager = assetManager;
    }

    public Spatial create(String name) {
        Node visual = new Node("Visual");
        Node model = (Node) assetManager.loadModel("Models/" + name + ".j3o");
        visual.attachChild(model);
        return visual;
    }
}

This implies that we have our models unter "Project Assets" (in real it the folder's name is "assets") and there in the folder "Models". I think needs not much explanation I let speak the code for itself. Of course you can do your factory better, more OO or not at all, I don't care.

Quick and dirty

So now we want to see that ship on the screen. For this we start with the visual system, I called it VisualAppState and it looks as follow

package mygame;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;

public class VisualAppState extends AbstractAppState {
    private SimpleApplication app;
    private ModelFactory modelFactory;
 
    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        this.app = (SimpleApplication) app;
        app.getCamera().lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
        app.getCamera().setLocation(new Vector3f(0, 0, 60));
        DirectionalLight light = new DirectionalLight();
        light.setDirection(new Vector3f(1, 1, -1));
        this.app.getRootNode().addLight(light);
        modelFactory = new ModelFactory(this.app.getAssetManager());
        Spatial myVisual = modelFactory.create("SpaceShip");
        this.app.getRootNode().attachChild(myVisual);
    }

    @Override
    public void cleanup() {
    }

    @Override
    public void update(float tpf) {
    }
}

I assume your space ship is stored in the assets/Models/SpaceShip.j3o file.

If your model do have material then you will need a light, I took a directional light here.
And register that in the Main class as follow.

public Main() {
    super(new VisualAppState(),
            new EntityDataState());
}

That's it, fire up your game with F6 and see. I agree there is not much about a ES so far but we are now prepared to do the first real entity system, actually we have to put some flesh on the VisualAppState and add some kind of a GameAppState. Stay tuned the next part will comming soon.

Comments

comments powered by Disqus