Lens #9: The Lens of the Elemental Tetrad

The next lens I want to look through on our game "All Fucked Up" is the lens of elemental tetrad from the book The Art of Game Design: A Book of Lenses written by Jesse Schell. Elemental tetrad is the diamond shape of the four elements aesthetics, story, technology, and mechanics.

I will now answer the following four questions:

  • Is my game design using all four elements?
  • Could my design be improved by enhancing elements?
  • Are the four elements in harmony, reinforcing each other and working together toward a common theme?

Our game focus on gamepads rather than keyboard or joystick. For testing and development purposes it is currently also possible to play it with the keyboard. But the main focus will be the gamepad. We will not go into a twin-stick game, so you can only shoot vertical.

The mechanics are laid out for gamepads to have good control over the character while jumping around. As well all the buttons to make reload, switch weapon from rifle to handgun smooth. The mechanics are rather simple, you can walk, jump, double jump, wall jump, shoot, reload, and switch weapons. The levels support this as you often close to enemies you have to switch to the handgun, as rifles don’t work on man to man combat. As well you will need the reload mechanics to have always enough ammo when you enter a platform.

The story is around a man who has been dropped into a weird world full of people who want to shoot him. To go back to his normal life he has to go through all the levels. The man has amazing gun skills he didn’t even know.

The aesthetics are rather simple. We focus more on the little things to add love to the game, like shell ejection. Every weapon does have its shell ejection and reload behavior.

We plan to add more little animations like dust, falling leaves, papers which whirl up if the characters rush through a room, flickering neon, and many more. We focus on these little things and keep the overall graphic design simple.

The very simple graphics might need improvement, I’m not sure if it is enough to just have these little animations. It’s not really up to date graphic design but unique. The animations of the guns reinforce the theme a lot as it is kind of a gun porn game. The cuteness of the characters helps to improve the overall aesthetics I think.
The levels drive the story and the goal is that the player wants to finish a level to see what is coming in the next level. We plan to have different worlds, starting with a grass and bunker world. But as well an office world, tower world, and more. The gamepad should make it more attractive to game shows, it’s much more likely that somebody picks up a gamepad and give it a try than sit down in front of a computer and uses the keyboard. Gamepads also need much less introduction as it is so much more natural to play.


I get commissions for purchases made through the following links, I only put stuff in this section I also would buy or I already bought

ECS Pattern: Damage System

Writing games is often about dealing with health and damage. With entity component system this is fun to do. Furthermore you can give any entity health or damage any time, it is literally only adding the health or damage or both component to that entity.

Bullets And Characters

To handle health and damage we need health, damage, position, and shape components for the involved entities. A bullet usually does have a damage component besides a position and its shape. A character usually does have a health component besides a position and its shape. The damage system handles these entities and calculates the new damage and health of the involved entities. I usually remove the damage entity a soon it hits something, but if you think of a spell or an ax this is not necessarily true. For a bullet, you might add a very short decay component to let the bullet have a physical impact on the body it hits (read here about the decay pattern) before it vanishes.

A Monkey And Doctor Zaius

For this example I use jMonkeyEngine and Zay-ES. Zay-ES features EntityContainer with an update method which takes care of all created, updated and deleted entity and it provides an array of all entities which are currently in the set (not removed). This makes the code super easy. If your ECS system doesn’t have then you probably have to do a bit more leg work.

Components

I shortly introduce the components we will need. The components are pure data without methods that operate on this data which is very different from the OO programming paradigm.

Health

We just store the amount of health in this component. Furthermore, we define -1 as immortal (for concrete walls for example).

public class Health implements EntityComponent {
    static public int IMMORTAL = -1;
    private Integer health;
    public Health(Integer health) {
        this.health = health;
    }
    public Integer getHealth() {
        return health;
    }
}

Damage

We store the amount of damage in this component, that’s it.

public class Damage implements EntityComponent {
    private final int damage;
    public Damage(int damage) {
        this.damage = damage;
    }
    public int getDamage() {
        return damage;
    }
}

Position

And the position component holds the current 3D location (read my first ECS post how to move objects around).

public class Position implements EntityComponent {
    private final Vec3d location;
    public Position(Vec3d loc) {
        this.location = loc;
    }
    public Vec3d getLocation() {
        return location;
    }
}

Shape

And then finally the shape. To make our life simple we just have sphere objects with a radius.

public class SphereShape implements EntityComponent {
    private double radius;
    public SphereShape(double radius) {
        this.radius = radius;
    }
    public double getRadius() {
        return radius;
    }
}

Damage Entity Set

In the damage system we keep two sets of entities. The first set called the DamageContainer handles all entities with a position, shape and damage component.

    private class DamageData {

        EntityId entityId;
        AABB aabb;
        Vec3d location;
        int amount;
    }

    private class DamageContainer extends EntityContainer<DamageData> {

        DamageContainer(EntityData ed) {
            super(ed, Damage.class, Position.class, SphereShape.class);
        }

        Stream<DamageData> stream() {
            return Arrays.stream(getArray());
        }

        @Override
        protected DamageData addObject(Entity entity) {
            DamageData result = new DamageData();
            result.entityId = entity.getId();
            result.amount = entity.get(Damage.class).getDamage();
            updateObject(result, entity);
            return result;
        }

        @Override
        protected void updateObject(DamageData data, Entity entity) {
            SphereShape sphereShape = entity.get(SphereShape.class);
            data.location = entity.get(Position.class).getLocation();
            Vector2 center = new Vector2(data.location.x, data.location.y);
            data.aabb = new AABB(center, sphereShape.getRadius());
        }

        @Override
        protected void removeObject(DamageData t, Entity entity) {
        }
    }

The data we want is the entity id (to be able to get rid of), the location, amount of damage, and an AABB object for simple collision calculation.

Health Entity Set

On the other hand, we have the health set we call it HealthContainer and it goes like this

    private class HealthData {

        EntityId entityId;
        AABB aabb;
        int amount;
    }

    private class HealthContainer extends EntityContainer<HealthData> {

        HealthContainer(EntityData ed) {
            super(ed, Health.class, Position.class, RectangleShape.class);
        }

        Stream<HealthData> stream() {
            return Arrays.stream(getArray());
        }

        @Override
        protected HealthData addObject(Entity entity) {
            HealthData result = new HealthData();
            result.entityId = entity.getId();
            result.amount = entity.get(Health.class).getHealth();
            updateObject(result, entity);
            return result;
        }

        @Override
        protected void updateObject(HealthData data, Entity entity) {
            SphereShape sphereShape = entity.get(SphereShape.class);
            data.location = entity.get(Position.class).getLocation();
            Vector2 center = new Vector2(data.location.x, data.location.y);
            data.aabb = new AABB(center, sphereShape.getRadius());
        }

        @Override
        protected void removeObject(HealthData t, Entity entity) {
        }
    }

The data we want is the same as for the DamageContainer, the entity id (to be able to get rid of it as well), the location, amount of health and an AABB object for simple collision calculation.

The Update Loop

In the update loop we now update the HealthContainer and the DamageContainer and then we loop over all health entities and inside that we loop over all damage entities and calculate the new Health and Damage component. In our case, we remove the Damage component from the entities on collision and only calculate the new Health component.

   @Override
    public void update(float tpf) {
        healthConatiner.update();
        damageContainer.update();

        healthConatiner.stream().forEach(h -> {
            damageContainer.stream()
                    .filter(d -> !d.parentId.equals(h.entityId))
                    .filter(d -> d.aabb.overlaps(h.aabb))
                    .forEach(d -> {
                        handleHealthAndDamage(h, d);
                    });
        });
    }

Calculus

Let’s look into how we handle health and damage. First we check if the health entity is immortal like a concrete wall and skip the calculation. If the health entity is not immortal we calculate the new health and remove the health entity if the health is equal or below zero. In the end we remove the damage entity as it is used up on collision.

    private void handleHealthAndDamage(HealthData health, DamageData damage) {
        if (health.amount != Health.IMMORTAL) {
            health.amount = health.amount - damage.amount;
            if (health.amount > 0) {
                ed.setComponent(health.entityId, new Health(health.amount));
            } else {
                ed.removeEntity(health.entityId);
            }
        }
        ed.removeEntity(damage.entityId);
    }

Benefit

If your game designer has the famous idea at the last minute possible that your weapons should also be able to destroy the furniture you will not spend hours or days making this happen as you simply can add health to these furniture entities and you are done. Because the code is already in place which handles these entities on collision. This is the actual beauty of the entity component system.

I’m Reading

I’m currently reading the self-published Data Oriented Design from Richard Fabian to get me a deeper insight how to design games with entity component system or as the book title says data-oriented design. A very interesting and cool topic so much different from what I learned with OO approaches. Data-oriented design not only improves the execution time but also your developing performance. Counts especially for games with its short update cycle and the game data which is changing constantly.

The End

You can extend and tweak this example as you like. Go wild.

In case you have questions or suggestions, let me know in the comments below.

History Back and Forth

Whenever you write a level editor or maybe a turn-based game (like chess) it would be of great help having undo and redo functionality at hand.

Based on the Game Programming Patterns book from Robert Nystrom I did like to implement an undo/redo history class. The design pattern is based on the Design Patterns book of the gang of four.

Side Note

I use this programming pattern for my test-driven development workshop, I do occasionally. If you are interested in that, just let me know in the comments below.

Implementation

Introduction

A command can be anything from move one square to shoot, cast a spell, and more. The command must encapsulate everything needed to execute and to undo his action. Every command is an own instance. The commands then get stored in a history object which I will show you in this blog post.

Command Interface

The whole thing starts with a command interface which looks like this

package mygame;

public interface Command {
    public void execute();
    public void undo();
}

The only thing you need in command is execute and undo. With that, you can even make a redo. Really?

History Execute, Undo, and Redo

I’ll show you the idea of the history implementation.

package myexample;

public History {
    public void execute(Command command) {}
    public void undo() {}
    public void redo() {}
}

To make the whole thing work you need to encapsulate everything needed to execute the command in your command object, which is of great importance.

For the implementation of the history class, I used LinkedList as it was the simplest way I found for this. Of course, you can implement as well some ring buffer structure using plain arrays, but I did not have the nerve for this.

Ok, let’s start with the basics. First of all, we need to execute the command like this

public void execute(Command command) {
    command.execute();
}

That wasn’t too hard, but to be able to undo it, we need to hold that command instance in a data structure, a linked list in my case.

    private LinkedList<Command> commands;
    public History(int size) throws InstantiationException {
        commands = new LinkedList<>();
    }

    public void execute(Command command) {
        commands.add(command);
        command.execute();
    }

    public void undo() {
        commands.removeLast().undo();
    }

Oh, wait what if I undo when the list is empty? Yes, it fails, so we need some guards to protect it.

    public void undo() {
        if (!commands.isEmpty()) {
            commands.removeLast().undo();
        }
    }

Improvments

That looks not that bad and it works already. But let’s improve it a little more. For example, does this implementation not limit the number of commands in our history, so let’s introduce a size.

    private int size;

    public History(int size) throws InstantiationException {
        if (size == 0) {
            throw new InstantiationException("0 is not a valid history size");
        }
        this.size = size;
        commands = new LinkedList<>();
    }

And now we have to improve the execute method with this size information, by dropping the first command f we hit the capacity of our history.

    public void execute(Command command) {
        if (commands.size() >= size) {
            commands.removeFirst();
        }
        commands.add(command);
        command.execute();
    }

Redo

A redo would be cool and it turns out to be very simple. We need a second linked list for book-keeping.

    private LinkedList<Command> redoCommands;
    public History(int size) throws InstantiationException {
        // ...
        redoCommands = new LinkedList<>();
    }

Then we have to store all undoed commands in that redoCommands list.

    public void undo() {
        if (!commands.isEmpty()) {
            Command command = commands.removeLast();
            redoCommands.add(command);
            command.undo();
        }
    }

Now we are prepared for the redo method

    public void redo() {
        if (!redoCommands.isEmpty()) {
            execute(redoCommands.removeLast());
        }
    }

One small problem we have to solve, what if we did undo 2 times and then call execute and after that call redo? Yeah, it will end up in redo commands which actually should be cleared. So let’s clear the redo command list on execute. This is not that easy as the redo itself calls execute to have the command book-keeping for free. The easiest way is to have an internal execute which is called by execute and redo and only execute clears the redo commands list.

    public void execute(Command command) {
        redoCommands.clear();
        internalExecute(command);
    }

    public void redo() {
        if (!redoCommands.isEmpty()) {
            internalExecute(redoCommands.removeLast());
        }
    }

    private void internalExecute(Command command) {
        if (commands.size() >= size) {
            commands.removeFirst();
        }
        commands.add(command);
        command.execute();
    }

Copy & Paste Code

And to make it a simple copy&past task to take this into your project here the things you need

package myexample;

import java.util.LinkedList;

public class History {

    private LinkedList<Command> commands;
    private LinkedList<Command> redoCommands;
    private int size;

    public History(int size) throws InstantiationException {
        if (size == 0) {
            throw new InstantiationException("0 is not a valid history size");
        }
        this.size = size;
        commands = new LinkedList<>();
        redoCommands = new LinkedList<>();
    }

    public void execute(Command command) {
        redoCommands.clear();
        internalExecute(redoCommands.removeLast());
    }

    public void redo() {
        if (!redoCommands.isEmpty()) {
            internalExecute(redoCommands.removeLast());
        }
    }

    public void internalExecute(Command command) {
        if (commands.size() >= size) {
            commands.removeFirst();
        }
        commands.add(command);
        command.execute();
    }

    public void undo() {
        if (!commands.isEmpty()) {
            Command command = commands.removeLast();
            redoCommands.add(command);
            command.undo();
        }
    }
}

That’s it, folks.

Related Books

This design pattern is based on these two books, I have both ot them, I like the game programming patterns better than the original but the orignal is almost a must have a for any programmer.