LibGDX jam second weekend

Quick recap

The second weekend have I focus on game play. I have a working AI that is targeting the player, it is very very simple so far. I have done input to a ship so I can steer it. There are physics so the ships can move and turn. I have a weapon system so I can shoot projectiles. I can detect hits and an particle system are running when a hit is accuring. The game look like this.

libgdxjam

Moving from Entity to ECS

Going from an Entity that inheritance a general object to an Entity Component System have been very hard for me. I do like the simple entity for small games. It is very fast to get started with. The complexity are a lot smaller then an ECS. For small games I think I will stick with the entity setup. The problem is that I need to know almost the entire game before started because it is very inflexible to work with. But for fast small thing I do like it. It is very simple.

I have been working with Artemis now this week and the complexity is going up a lot compared to the simpler entity solution. But the more I work with ECS the more I start to like it. The big question for me at the moment is how many components should I have and how many systems. I have read this article and he suggest that when you move from an entity inheritance to an ECS you have a one-to-one ratio between component and systems. Then when you get more used to it you can get it more to what you need. But I think when you start a project there are a good aim to have a one-to-one ratio.

Components

All the components can be found here for easier understanding.

The components I have created so far are a Transform that handle where the entity are, the z-value on what plane things should be running manly for rendering, rotation and the scale. I Texture that just hold a sprite. Input that tell that this entity should handle manual inputs. AI that tell that the entity should be handled by a computer. Physic that handle velocity (what direction to move), speed and rotation speed. Explosion that just handle a particle effect, maybe I should make a more general particle component. Bomb that is a ball that is flying in a straight line, it have how long it will live and how much damage it should do. I don’t know how I should be doing with the different weapons so I’m doing different component for every weapons. Last have I the Ship, this have the health, max speed, max rotation speed and if can stop when you don’t press the button any more.

This is all the component I’m using at the moment. I will need some more weapons components. I don’t know if I should go with many ships also, or if I can have just one ship.

Systems

Now to the fun part. All my systems and how I’m thinking with them.

In my GameLoopSystemInvocationStratey class have I only added so my none Logic system get the remaining delta. This is used in the render to get a smoother animation but with fewer logic ticks. This meen that I can have a logic calculation on 20 FPS but I run the graphics on 60 FPS.

My SortedIteratingSystem are not changed, but my SortedRenderSystem have some addition. I have added so the camera are moving with the player ship, this is made in the begin method. In process method have I added delta calculation if the graphics running faster then the logics to make it look smoother. I have added particles with an explosion. I have also change the ZComparator to order with the highest number will be the one furthest up.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/SortedRenderSystem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Override
    protected void begin() {
        if (jamSpace.camera != null) {
            AspectSubscriptionManager asm = world.getAspectSubscriptionManager();
            EntitySubscription es = asm.get(Aspect.all(InputComp.class, Transform2DComp.class));
            if (es.getEntities().size() == 1) {
                Transform2DComp t2d = world.getEntity(es.getEntities().get(0)).getComponent(Transform2DComp.class);
                if (physic.has(es.getEntities().get(0))) {
                    PhysicComp physicComp = physic.get(es.getEntities().get(0));
                    jamSpace.camera.position.set(t2d.position.x + (physicComp.velocity.x * physicComp.speed * world.getDelta()), t2d.position.y + (physicComp.velocity.y * physicComp.speed * world.getDelta()), 0);
                } else {
                    jamSpace.camera.position.set(t2d.position.x, t2d.position.y, 0);
                }
                jamSpace.camera.update();
            }
            if (jamSpace.batch != null) {
                jamSpace.camera.update();
                jamSpace.batch.setProjectionMatrix(jamSpace.camera.combined);
                jamSpace.batch.begin();
            }
        }
    }
 
    @Override
    protected void process(Entity entity) {
        if (jamSpace.batch != null) {
            Transform2DComp transformComp = transform2d.get(entity);
            if (texture.has(entity)) {
                TextureComp textureComp = texture.get(entity);
                if (textureComp.sprite != null) {
                    if (physic.has(entity)) {
                        PhysicComp physicComp = physic.get(entity);
                        textureComp.sprite.setPosition(transformComp.position.x + (physicComp.velocity.x * physicComp.speed * world.getDelta()), transformComp.position.y + (physicComp.velocity.y * physicComp.speed * world.getDelta()));
                    } else {
                        textureComp.sprite.setPosition(transformComp.position.x, transformComp.position.y);
                    }
                    textureComp.sprite.setScale(transformComp.scale.x, transformComp.scale.y);
                    textureComp.sprite.setRotation(transformComp.rotation);
                    textureComp.sprite.draw(jamSpace.batch);
                }
            }
            if (explosion.has(entity)) {
                ExplosionComp explosionComp = explosion.get(entity);
                explosionComp.effect.setPosition(transformComp.position.x, transformComp.position.y);
                explosionComp.effect.draw(jamSpace.batch, world.getDelta());
                if (explosionComp.effect.isComplete()) {
                    world.delete(entity.getId());
                }
            }
        }
    }

My PhysicsSystem are calculating where moving object should move. Not much to think about here.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/PhysicsSystem.java
1
2
3
4
5
6
    protected void process(int entityId) {
        Transform2DComp transform2DComp = transform2d.get(entityId);
        PhysicComp physicComp = physics.get(entityId);
        transform2DComp.rotation = transform2DComp.rotation + (physicComp.rotationSpeed * delta);
        transform2DComp.position.add(physicComp.velocity.x * physicComp.speed * delta, physicComp.velocity.y * physicComp.speed * delta);
    }

My InputSystem handle all the inputs a human player can make. At the moment have I hard coded it to only work with one weapon type. So this will be the next thing to look at.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/InputSystem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    protected void process(int entityId) {
        PhysicComp physicComp = physic.get(entityId);
        Transform2DComp transform2DComp = transform2d.get(entityId);
        ShipComp shipComp = ship.get(entityId);
        if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
            if (physicComp.rotationSpeed < shipComp.maxRotationSpeed) { physicComp.rotationSpeed += shipComp.maxRotationSpeed * delta; } } else if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) { if (physicComp.rotationSpeed > -shipComp.maxRotationSpeed) {
                physicComp.rotationSpeed += -shipComp.maxRotationSpeed * delta;
            }
        } else {
            physicComp.rotationSpeed = 0;
        }
        if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
            physicComp.speed = shipComp.maxSpeed;
            float x = MathUtils.cosDeg(transform2DComp.rotation);
            float y = MathUtils.sinDeg(transform2DComp.rotation);
            physicComp.velocity.set(x, y);
        } else {
            if (shipComp.directStop) {
                physicComp.velocity.set(0,0);
            }
        }
        if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE)) {
            float x = MathUtils.cosDeg(transform2DComp.rotation);
            float y = MathUtils.sinDeg(transform2DComp.rotation);
            Vector2 position = new Vector2(transform2DComp.position);
            position.x += 36 + (x * 36);
            position.y += 55 + (y * 55);
            Entity fire = world.createEntity();
            fire.edit().add(new TextureComp(new Sprite(new Texture("weapons/bomb.png"))))
                    .add(new Transform2DComp(position, 2, new Vector2(1, 1), transform2DComp.rotation))
                    .add(new PhysicComp(new Vector2(x, y), 500, 0))
                    .add(new BombComp(2));
 
        }
    }

My AISystem just handle computer ships. It try to find the closest human and attack it. Need to make this more intelligent later on. So far it just turn to the player and go. I need to make it swing or slowly rotate against the player and not just jump to where the player is. This will give the computer an unfair advantage.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/AISystem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    @Override
    protected void begin() {
        closestShip = -1f;
    }
 
    @Override
    protected void process(int entityId) {
        AspectSubscriptionManager asm = world.getAspectSubscriptionManager();
        EntitySubscription es = asm.get(Aspect.all(ShipComp.class, Transform2DComp.class));
        Entity ep = world.getEntity(entityId);
        Entity toShip = null;
        for (int i = es.getEntities().size() - 1; i >= 0; i--) {
            Entity e = world.getEntity(es.getEntities().get(i));
            if (e.getId() != entityId) {
                float distance = e.getComponent(Transform2DComp.class).position.dst(ep.getComponent(Transform2DComp.class).position);
                if (closestShip == -1 || distance < closestShip) {
                    closestShip = distance;
                    toShip = e;
                }
            }
        }
        if (toShip != null) {
            float degree = toShip.getComponent(Transform2DComp.class).position.cpy().sub(ep.getComponent(Transform2DComp.class).position).angle();
            ep.getComponent(Transform2DComp.class).rotation = degree;
            float x = MathUtils.cosDeg(degree);
            float y = MathUtils.sinDeg(degree);
            ep.getComponent(PhysicComp.class).velocity.set(x, y);
        }
 
    }

The last system for now are the WeaponSystem. So far it look like I will be building one system that handle all the different weapons instead of one system for every type of weapon. This might be a dumb thing to do. But I think it’s not that hard to split it if I need to do that later. When I’m looking at it while writing I think this isn’t the best way to go. Might split it tomorrow.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/WeaponSystem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected void process(int entityId) {
        if (bomb.has(entityId)) {
            BombComp bombComp = bomb.get(entityId);
            if (bombComp.livedTime > bombComp.maxLiveTime) {
                world.delete(entityId);
            }
            bombComp.livedTime += delta;
 
            TextureComp bombTexture = texture.get(entityId);
            AspectSubscriptionManager asm = world.getAspectSubscriptionManager();
            EntitySubscription es = asm.get(Aspect.all(ShipComp.class, TextureComp.class, AIComp.class));
            for (int i = es.getEntities().size() - 1; i >= 0; i--) {
                Entity shipE = world.getEntity(es.getEntities().get(i));
                TextureComp shipTexture = texture.get(shipE);
                if (shipTexture.sprite.getBoundingRectangle().overlaps(bombTexture.sprite.getBoundingRectangle())) {
                    ShipComp shipComp = ship.get(shipE);
                    shipComp.health -= MathUtils.random(bombComp.minDmg, bombComp.maxDmg);
                    world.delete(entityId);
                    if (shipComp.health < 0) {
                        world.delete(shipE.getId());
                    }
 
                    Entity explosion = world.createEntity();
                    explosion.edit().add(new Transform2DComp(new Vector2(bombTexture.sprite.getX(), bombTexture.sprite.getY()), 10, new Vector2(1,1), 0))
                            .add(new ExplosionComp());
                    explosion.getComponent(ExplosionComp.class).effect.start();
                    System.out.println("health: " + shipComp.health);
                }
            }
        }
    }

I do hope you find this post some what informative in the problem I have. There aren’t that many explanation how I have solved stuff. Just some feelings I have against ECS and moving from an Entity object. Hopefully things have sattle more to next weekend so I can give more information how I solve things. If you have any thoughts just give a comment and I will help as best as I can.

Happy Coding!

LibGDX game jam first weekend

This weekend have I set up the project, I have started to use Artemis. This is my first time so I’m little lost how big every component should be and how big every system should be. You can find all the code on my GitHub page, and you can find all my planing on trello. I don’t have any direkt graphics yet. But it is the next thing for me.

Main core class

I like to be able to change screen very easy so in my main class I add all my screens as public and then in the constractor on every screen I add my main core class. I also have some objects that all the screens need. I have the viewport/camera and batch public also for easy access.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/LibGDXJamSpace.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// One batch
public SpriteBatch batch;
 
// One camera, orthographic to handle 2d, and a viewport
public Camera camera;
public Viewport viewport;
 
// Arthemis gameworld
public World arthWorld;
 
// Screens
public SplashScreen splashScreen;
public GameScreen gameScreen;
 
// Handle all assets
public AssetManager assetManager;

All the creation of objects are stright forward. I create all my screens, easy to add more. I add my camera and move it so 0,0 are in the bottom left corner and not center screen. I haven’t decided what viewport I should use yet so I use a streatched viewport to start with.
In the config for artemis I’m adding my own invocation strategy class. This is so I can keep a steady update on all my logic systems and run as fast as I can on my other systems (render for example). The number in my systems (should be a private final value) are the FPS I want to simulate everything in, in my case 20 FPS. I’m thinking if I have time to make it a network game and then I don’t want the logic to be to fast but Still have the render look smooth.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/LibGDXJamSpace.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public void create () {
	assetManager = new AssetManager();
 
	// create all screens
	splashScreen = new SplashScreen(this);
	gameScreen = new GameScreen(this);
 
	// Create camera
	camera = new OrthographicCamera();
	((OrthographicCamera)camera).setToOrtho(false);
	viewport = new StretchViewport(width, height, camera);
	camera.position.set(viewport.getWorldWidth() / 2f, viewport.getWorldHeight() / 2f, 0);
 
	batch = new SpriteBatch();
 
	WorldConfiguration config = new WorldConfigurationBuilder()
			.with(new SortedRenderSystem(this), new PhysicsSystem(20), new InputSystem(this, 20))
			//.with(new RenderSys(), new EditingSys(), new ScriptSys())
			.register(new GameLoopSystemInvocationStrategy(20))
			.build();
	arthWorld = new World(config);
	setScreen(splashScreen);
 
	testData();
}

System classes

In my class GameLoopSystemInvocationStrategy is the strategy to handle a constant FPS on all the logic system and how to handle all the system that isn’t time critical, like rendering. I did find this on GitHub and I have been modifing it to work better for me. This is constructed from the idé from Gaffersons blog.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/GameLoopSystemInvocationStrategy.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class GameLoopSystemInvocationStrategy extends SystemInvocationStrategy {
 
 
    private float timeToProgress;
    private float accumulator  = 0;
    private float simAccumulator = 0;
 
    private final Array logicMarkedSystems;
    private final Array otherSystems;
 
    private boolean systemsSorted = false;
 
    public GameLoopSystemInvocationStrategy(float logicFPS) {
        timeToProgress = 1 / logicFPS;
        logicMarkedSystems = new Array();
        otherSystems = new Array();
    }
 
    @Override
    protected void process(Bag systems) {
        if (!systemsSorted) {
            sortSystems(systems);
        }
        accumulator += world.delta;
        while (accumulator >= timeToProgress) {
            for (int i = 0; i < logicMarkedSystems.size; i++) {
                /**
                 * Make sure your systems keep the current state before calculating the new state
                 * else you cannot interpolate later on when rendering
                 */
                logicMarkedSystems.get(i).process();
                updateEntityStates();
            }
            accumulator -= timeToProgress;
            simAccumulator = 0;
        }
        for (int i = 0; i < otherSystems.size; i++) {
            /**
             * Use the kept state from the logic above and interpolate with the current state within your render systems.
             */
            otherSystems.get(i).process();
            updateEntityStates();
        }
    }
 
    private void sortSystems(Bag systems) {
        if (!systemsSorted) {
            Object[] systemsData = systems.getData();
            for (int i = 0, s = systems.size(); s > i; i++) {
                BaseSystem system = (BaseSystem) systemsData[i];
                if (system instanceof LogicRenderMarker) {
                    logicMarkedSystems.add(system);
                } else {
                    otherSystems.add(system);
                }
            }
            systemsSorted = true;
        }
    }
}

For my render system I did a general sorting system that make it able to sort all my assets on the Z value. This make it easyer to display things in the correct order. I wanted to send a Comparable argument into the constructor but I couldn’t get that to work. So I had to make a setComparator method. It’s not as elegant but it works.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/SortedIteratingSystem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public abstract class SortedIteratingSystem extends BaseEntitySystem {
 
    private final Array sortedEntities = new Array();
    private Comparator comparator;
 
    public SortedIteratingSystem(Aspect.Builder aspect) {
        super(aspect);
    }
 
    public void setComparator(Comparator comparator) {
        this.comparator = comparator;
    }
 
    protected abstract void process(Entity entity);
 
    @Override
    protected final void processSystem() {
        for (int i = sortedEntities.size - 1; i >= 0; i--) {
            process(sortedEntities.get(i));
        }
    }
 
    @Override
    protected void inserted(int entityId) {
        sortedEntities.add(world.getEntity(entityId));
        sortedEntities.sort(comparator);
    }
 
    @Override
    protected void removed(int entityId) {
        sortedEntities.removeValue(world.getEntity(entityId), false);
        sortedEntities.sort(comparator);
    }
}

Then my render class just use this sorting class to display everything on the screen.

/LibGDXJamSpace/blob/master/core/src/net/corpwar/game/libgdxjam/systems/SortedRenderSystem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class SortedRenderSystem extends SortedIteratingSystem {
 
    private LibGDXJamSpace jamSpace;
    private ComponentMapper transform2d;
    private ComponentMapper texture;
 
    public SortedRenderSystem(LibGDXJamSpace jamSpace) {
        super(Aspect.all(Transform2DComp.class, TextureComp.class));
        setComparator(new ZComparator());
        this.jamSpace = jamSpace;
    }
 
    @Override
    protected void begin() {
        if (jamSpace.batch != null) {
            jamSpace.camera.update();
            jamSpace.batch.setProjectionMatrix(jamSpace.camera.combined);
            jamSpace.batch.begin();
        }
    }
 
    @Override
    protected void process(Entity entity) {
        if (jamSpace.batch != null) {
            TextureComp textureComp = texture.get(entity);
            Transform2DComp transformComp = transform2d.get(entity);
            if (textureComp.sprite != null) {
                textureComp.sprite.setPosition(transformComp.position.x, transformComp.position.y);
                textureComp.sprite.setScale(transformComp.scale.x, transformComp.scale.y);
                textureComp.sprite.setRotation(transformComp.rotation);
                textureComp.sprite.draw(jamSpace.batch);
            }
        }
    }
 
    @Override
    protected void end() {
        if (jamSpace.batch != null) {
            jamSpace.batch.end();
        }
    }
 
    private class ZComparator implements Comparator {
        @Override
        public int compare(Entity e1, Entity e2) {
            return (int)Math.signum(transform2d.get(e1).zValue - transform2d.get(e2).zValue);
        }
    }
}

Nothing strange with the other systems. They have a small task on there hand. At the moment I’m thinking how I should get the input system to work in a good way. I’m little confused how I should do it. I have a kinda working solution. It’s easy to change othervise.

That’s it for this blog, Happy Coding everyone.

LibGDX game jam

Today have I been setting up a project in LibGDX. I will be using artemis as ECS. I will use trello to organize my project. You can find it here. The project will be a top down space shooter where you pilot a space ship. It will be a 2D game. I will use github as code system. I will use InkScape to do all the art. Hopefully will I do all my art my self. I will try to explain my design and how I solve different problems here.

Happy coding.