Lens #11: The Lens of Unification

The lens of unification of The Art of Game Design: A Book of Lenses is the next lens I want to look at through on our game All Fucked Up. The book was written by Jesse Schell and is one of the most useful books about game design with its unique approach of lenses.
Unification is about finding a theme for All Fucked Up and reinforce that theme.

  • What is the theme of All Fucked Up?
  • Do I reinforce that theme in all ways possible?

Finding the theme is an important part to design All Fucked Up. Because it will help me to filter all the things which do not fit with my theme. A great decision tool. But it’s not easy to find a theme.
My game is about guns and ammo. It feels good to fire the guns. It feels good to shoot stuff in pieces. The player should have a lot of occasions to fire the collected guns. The theme will be gun-related. The theme is also about being cool and funny, like Eddie Murphy or Will Smith. Being highly skilled with guns like John Wick or Jason Bourne. The theme is about being a guy which is at the wrong time at the wrong place.

"Guns, Ammo, and Being a Funny Hero"

The guns feel good, the ammo has to fit the guns. The focus is on guns, ammo, and the hero. The environment should not be too distractive. Guns fired in the dark looks very cool. The following will reinforce the theme:

  • Dark levels
  • The guns with its kickback, shell ejection, and power
  • The relatively weak opponents make the guns feel very powerful
  • To have an excuse to use the weapons there will be plenty of enemies to shoot
  • The hero is minion like and funny, he loves collecting ammo and is skilled with the guns
  • The enemies have to be mean to make the hero look funny if he screams in fear or surprise
  • Jump, double jump, wall jumps, roles (dashing) to have improved action scenes

I would like to have a different environment which fits the theme but very simple

  • Concrete
  • Fire
  • Neon light
  • Dark
  • Tubes

Enforce the destruction with contrast like silence, action free transitions, butterflies, crickets, and more


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.

Laughing While Shooting

The little hero starts to laugh when shooting a lot. It is bound to the number of rounds per second to make him start laughing, every weapon has his threshold. Or when he shoots two or more targets in a row.

It makes the game more fun.

I’m thinking of more little animations like the love it animation for picking up ammo. Currently, I’m thinking of a cursing animation, unfortunately, I can not use the hits I get because we die too easy and I don’t want to change that. He curses when he stucks somewhere maybe. Or when he struggles with something.