Skip to content
Andrew Potapov edited this page Jan 10, 2014 · 11 revisions

This is a quick tutorial to get you started using Artemis inside a libgdx game. We will implement a very simple example of an object moving across the screen.

There's a separate gdx-artemis-demo project that contains the source code for this tutorial tutorial in com.artemis.demo.quick. Feel free to check it out and play around with it.

Game Setup

First we will create a simple game class that will instantiate an Artemis world and execute the game loop.

import com.artemis.Entity;
import com.artemis.World;
import com.artemis.demo.quick.components.PositionComponent;
import com.artemis.demo.quick.components.VelocityComponent;
import com.artemis.demo.quick.systems.MovementSystem;
import com.artemis.demo.quick.systems.RenderingSystem;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;

public class QuickGameDemo extends Game {

    public static final int GAME_WIDTH = 400;
    public static final int GAME_HEIGHT = 400;

    World world;
    OrthographicCamera camera;

    /**
     * Initializes all of the in-game objects
     */
    @Override
    public void create() {

        // create a camera
        camera = new OrthographicCamera();

        createWorld();

        //create a few of entities
        createEntity(200, 200, 20, 20);
        createEntity(200, 200, -20, 20);
        createEntity(200, 200, 20, -20);
        createEntity(200, 200, -20, -20);
    }

    /**
     * Creates the world and adds entity systems to it.
     */
    protected void createWorld() {
        world = new World();

        world.setSystem(new MovementSystem());
        world.setSystem(new RenderingSystem(camera));

        world.initialize();
    }

    /**
     * Creates an entity with a given position (x, y) and velocity (vx, vy)
     */
    protected void createEntity(float x, float y, float vx, float vy) {
        Entity e = world.createEntity();

        PositionComponent pc = world.createComponent(PositionComponent.class);
        pc.position.set(x, y);
        e.addComponent(pc);

        VelocityComponent vc = world.createComponent(VelocityComponent.class);
        vc.velocity.set(vx, vy);
        e.addComponent(vc);

        e.addToWorld();
    }

    /**
     * Game loop.
     */
    @Override
    public void render() {
        world.setDelta(Gdx.graphics.getDeltaTime());
        world.process();
    }

    /**
     * Update the camera if the game screen is resized.
     */
    @Override
    public void resize(int width, int height) {
        float centerX = width / 2.0f;
        float centerY = height / 2.0f;

        this.camera.position.set(centerX, centerY, 0);
        this.camera.viewportWidth = width;
        this.camera.viewportHeight = height;
    }
}

Let's take a look at what's going on here:

  • Class MyGame extends the libgdx Game.
  • We have two member variables, an Artemis World and a libgdx OrthographicCamera.
  • In the create() method we initialize the camera, create the world and create a few entities.
  • createWorld() method instantiates the world, adds a couple of entity systems to it, and calls world.initialize(), which in turn initializes all the systems.
  • createEntity() method takes an entity's initial position and velocity and creates an entity with the appropriate components.
    • Note here that we need to create both the entity and the components by calling world.createEntity() and world.createComponent() respectively to ensure that both entities and components are pooled correctly.
  • The render() method executes the internals of the game loop by setting the delta of the world and calling world.process() to process the systems.
  • Lastly, the override of the resize method ensures that the camera is updated correctly.

Defining components

In QuickGameDemo class, we add two components to each entity. Let's take a look at their declarations.

import com.artemis.Component;
import com.badlogic.gdx.math.Vector2;

public class PositionComponent implements Component {

    public Vector2 position;

    public PositionComponent() {
        this(0, 0);
    }

    public PositionComponent(float x, float y) {
        position = new Vector2(x, y);
    }

    @Override
    public void reset() {
        position.set(0, 0);
    }
}
import com.artemis.Component;
import com.badlogic.gdx.math.Vector2;

public class VelocityComponent implements Component {

    public Vector2 velocity;

    public VelocityComponent() {
        this(0, 0);
    }

    public VelocityComponent(float x, float y) {
        velocity = new Vector2(x, y);
    }

    @Override
    public void reset() {
        velocity.set(0, 0);
    }
}

Note that components do not contain any logic. They are usually simple data container objects. If you are familiar with the MVC pattern. Think of components as Model objects.

In this example the components' member variables are given public access this is done for speed and simplicity of access. However, if you feel strongly about encapsulation, you are more than welcome to wrap the member variables with get/set functions appropriately. Other simple methods may be added here for example add/subtract. However, anything more complex should be relegated to Entity Systems.

Also note the required reset() method. It is called when a component is release back into the component pool.

Entity Systems

EnititySystems are where the game logic resides. To use the paradigm of MVC again, EntitySystems are the Controllers that operate on Components (Models).

Let's take a look at a system implementation:

Movement System

import com.artemis.Aspect;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.demo.quick.components.PositionComponent;
import com.artemis.demo.quick.components.VelocityComponent;
import com.artemis.systems.EntityProcessingSystem;

public class MovementSystem extends EntityProcessingSystem {

    ComponentMapper<PositionComponent> pm;
    ComponentMapper<VelocityComponent> vm;

    @SuppressWarnings("unchecked")
    public MovementSystem() {
        super(Aspect.getAspectForAll(
                PositionComponent.class, VelocityComponent.class));
    }

    @Override
    public void initialize() {
        pm = world.getMapper(PositionComponent.class);
        vm = world.getMapper(VelocityComponent.class);
    }

    @Override
    protected void process(Entity e) {
        // Get the components from the entity using component mappers.
        PositionComponent positionComponent = pm.get(e);
        VelocityComponent velocityComponent = vm.get(e);

        // Update the position.
        positionComponent.position.x += velocityComponent.velocity.x * world.getDelta();
        positionComponent.position.y += velocityComponent.velocity.y * world.getDelta();
    }
}

Let's break this down:

  • First MovementSystem extends EntityProcessingSystem. This allows us to process appropriate Entities one at a time.
  • We declare a couple of ComponentMappers which allow us to access components of a specific entity. The generic specifier indicates which Component a mapper will be retrieving.
  • To make sure that the Movement system only processes entities that have a position and a velocity. We declare an Aspect to the super constructor indicating that entities are required to have all of the specified components: super(Aspect.getAspectForAll(PositionComponent.class, VelocityComponent.class));
  • In order to be able to use ComponentMappers, they need to be retrieved from the Artemis world. And we do so in the initialize() method. This method will be called during the world.initialize() call.
  • Lastly, the process() call get's executed every time world.process() gets called. Artemis automatically sorts the entities which this particular system cares about and passes them to the process() method.
  • Inside of the process() method we use the ComponentMappers to retrieve the position and velocity Components for the provided entity.
  • We then update the position component with velocity and the elapsed delta.

Rendering System

Rendering system is slightly more complicated and uses OpenGL to render our entities on the screen.

import com.artemis.Aspect;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.demo.quick.components.PositionComponent;
import com.artemis.demo.quick.components.VelocityComponent;
import com.artemis.systems.EntitySystem;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.utils.Array;

public class RenderingSystem extends EntitySystem {

    ComponentMapper<PositionComponent> pm;

    OrthographicCamera camera;
    ShapeRenderer shapeRenderer;

    @SuppressWarnings("unchecked")
    public RenderingSystem(OrthographicCamera camera) {
        super(Aspect.getAspectForAll(
                PositionComponent.class, VelocityComponent.class));

        this.camera = camera;
        this.shapeRenderer = new ShapeRenderer();
        shapeRenderer.begin(ShapeType.Filled);
        shapeRenderer.setColor(1f, 1f, 1f, 1f);
    }

    @Override
    public void initialize() {
        pm = world.getMapper(PositionComponent.class);
    }

    @Override
    protected void processEntities(Array<Entity> entities) {
        // clear the screen
        Gdx.gl.glClearColor(0, 0, 0, 0);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        // update the camera and the shape renderer
        camera.update();
        shapeRenderer.setProjectionMatrix(camera.combined);
        shapeRenderer.identity();

        // render each entity
        for (Entity entity : entities) {
            PositionComponent positionComponent = pm.get(entity);
            shapeRenderer.circle(positionComponent.position.x, positionComponent.position.y, 10);
        }
    }
}
  • To render objects more efficiently RenderingSystem extends from EntitySystem, rather than EntityProcessingSystem. This allows us to perform a few setup operations before rendering the entities.
  • The constructor takes an OrthographicCamera, declares the Aspect for this system (filter for entities), and initializes a ShapeRenderer.
  • Similar to MovementSystem, we declare a PositionComponent ComponentMapper. and initialize it.
  • processEntities() method receives all appropriate Entities and renders them.
  • We first clear the screen
  • We then update the camera and shape renderer (in case the screen was resized)
  • We then proceed to draw a circle with radius of 10 at each entity's position.

That's it. Feel free to experiment with the code in the demo project.