Entity Component Systems in Elixir
Tags: elixir, programming
Published: 2018-02-16 12:49:40

---
title: Entity Component Systems in Elixir
published: true
description: Entity-Component-System (ECS) is a distributed and compositional architectural design pattern that is mostly used in game development.
tags: 
- elixir
- programming
---

![Entity Component System is used in the video game Caves of Qud](https://i.imgur.com/7yJLXLX.png)

Entity-Component-System (ECS) is a distributed and compositional architectural design pattern that is mostly used in game development. It enables flexible decoupling of domain-specific behaviour, which overcomes many of the drawbacks of traditional object-oriented inheritance.

Elixir is a dynamic, functional language built on top of the Erlang VM designed for building scalable and maintainable applications.

In this article, discover how we can use both ECS and Elixir in a novel approach to structure our programs beyond the class based inheritance paradigm.

> This is a follow-up article for my [Entity Component Systems](https://speakerdeck.com/yosriady/entity-component-systems) talk. The source code for my ECS implementation in Elixir is open source on [Github](https://github.com/yosriady/ecs).
>

<!--more-->

![Structure of my Entity Component Systems talk](https://i.imgur.com/Chhscxq.png)

# Drawbacks of class-based inheritance

The traditional way to build game worlds is to have an object-oriented hierarchy of game objects that model the world. However, even simple objects can end up with a large set of unused functionality. Consider the example below:

We're building a game engine, and we find ourselves with the below class hierarchy:

![Class hierarchy of a typical game engine](https://i.imgur.com/ycQyv8Y.png)

We have a base `GameObject`, which is subclassed by `Animal`. `Animal` is subclassed by `Bunny` and `Whale`, each with its own special behaviour `hop()` and `swim()` respectively. We also have a `Killer Whale`, which is a subclass of `Whale` than can `kill()`.

Let's try to introduce a new animal to our world:

![Ambiguity in our class hierarchy](https://i.imgur.com/tw03lpC.png)

> We want `Killer Bunny` to be able to `hop()` and `kill()`, but which class should `Killer Bunny` inherit from?

For languages / platforms with only single inheritance, we're out of luck. We'd have to move both `hop()` and `kill()` to some superclass such as `Animal` that `Killer Bunny` can then inherit. However, all other subclasses of `Animal` will inherit things they don't need. `Whale` inherits `hop()`; `Bunny` inherits `swim()` and `kill()`. Over time, `Animal` will become a god object with a massive set of behaviours.

Multiple inheritance doesn't do it either. Suppose `Killer Bunny` inherits from both `Bunny` and `Killer Whale`. `Killer Bunny` will inherit `swim()`, which is unneeded functionality.

We face a number of other issues:

**Rigid functionality:** Only `Killer Whale` can `kill()`. We can't change our mind later and make other animals `kill()` very easily. Behaviour is only available to classes that were specifically coded to support that behaviour. As the number of game entities grow, we face greater difficulty in finding a spot in the hierarchy to place new entities under.

![The diamond problem](https://www.programmerinterview.com/images/Diamond_inheritance.png)

**The Diamond Problem**: The "diamond problem" (sometimes referred to as the "deadly diamond of death") is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If there is a method in A that B and C have overridden, and D does not override it, then which version of the method does D inherit: that of B, or that of C?

**The Blob antipattern:** With inheritance, games end up with a huge single root class or some other leaf node with a large amount of functionality. Subclasses become overburdened with unneeded functionality.


## In closing

The difficulties mentioned above has plagued game developers for a long time, and Entity Component Systems attempts to remedy these annoyances. We'll learn about ECS in the next section.

# Entity Component Systems

There are three key abstractions in ECS:

- `Entity`
- `Component`
- `System`

We'll examine each in detail, starting from `Component`.

## Component

**The qualities or aspects of an entity.**

Components are minimal, reusable data objects that are plugged into entities to support some behaviour. A Component tags an entity with a single quality. A Component itself has no behaviour. Typically, it's implemented as a struct or dictionary.

Imagine we have a `Bunny` entity in our world:

![An example entity in entity component system](https://i.imgur.com/7usNJkI.png)

We can define bunnies as nothing more than an aggregation / collection of independent components. In the example above, a Bunny is 'composed' of components such as `Physical` and `Seeing`.

Each component support some behaviour. To illustrate, `Seeing` has attributes `sight_radius` to support a sight behaviour. Note however, that a Component themselves have no behaviour. Each component is simply a minimal data object.

## Entity

**An aggregation or container of components.**

Entities are solely the sum of its components. Entities are implemented as a globally unique IDs associated to a collection of Components. Note that Entities themselves have no actual data or behaviour. Each Component gives an Entity data to support some behaviour.

Let's look at our `Bunny` again:

![Entities are a container of components](https://i.imgur.com/Q7fX5M1.png)

See the dashed box around our components? That's the `Bunny` entity - nothing more than a container of components. We can define entities as an aggregation of any subset of components, like this `Carrot`:

![A carrot is another entity, with its own set of components](https://i.imgur.com/OzBVB92.png)

And this `Ghost`:

![Yet another entity](https://i.imgur.com/8Df8XTS.png)

An entity is little more than a collection of components.

> Some ECS implementations allow you to modify an entity's components collection at runtime. This allows you to "mutate" entities on the fly. For example, we could have a `Poisoned` component that makes entities tagged with this component to lose health over time. We can add and remove this component dynamically to inflict and cure poison. You might also have a 'blind' status effect that removes the `Seeing` component of entities it hits.

Up until this point, we haven't touched on any logic or behaviour. Entities are just an aggregation of components; Components are just data objects. Where does behaviour in ECS come from? They come from Systems.

## System

**Systems brings entities and components to life.**

![Systems brings entities and components to life](https://i.imgur.com/ZjqrfQI.png)

Systems enumerate over Components or groups of Components, updating their state according to an internal rule or external event. A way to think of Behaviour is as a change from one state to another. Let's see an example:

![Systems and their role](https://i.imgur.com/mMo6Ez1.png)

> Behaviour: "A bunny on a tree falls due to gravity."

How do we implement the above behaviour? We can make it so that `Placeable` Components with a `z` value more than 0 to decrease over time to 0.

> Behaviour: "Living beings age."

How do we implement the above behaviour? We can make it so that `Living` Components has its `age` value increase over time.

We create a dedicated System for each behaviour we wish to support. A `GravitySystem` enumerates over all `Placeable` Components; a `TimeSystem` enumarates over all `Living` components. Bear in mind that Systems operate on Components, not Entities.

## Data Flow in Entity-Component-System

To further cement your understanding of the pattern, let's see a typical data flow in this architecture:

![Entity Component System data flow](https://i.imgur.com/oR44M3E.png)

Each System listens to some event stream such as time or player input, and updates the states of its Components in response to that event and some internal rules. These continuously changing state is available for access by the Entities it is a part of, and thus result in behaviour.

> Another example: Suppose the player presses the "move left" key. PlayerInputSystem executes and detects the keypress, updating the Motion component. MotionSystem executes and "sees" the Motion for the entity is to the left, applying a Physics force to the left. The RenderSystem executes and reads the current position of the entity, and draws it according to the Spatial definition (which may include texture region / animation information).
>
> [*Introduction to Entity Systems*](https://github.com/junkdog/artemis-odb/wiki/Introduction-to-Entity-Systems)

## The spreadsheet analogy

Another way to think of ECS is as a relational table, like a spreadsheet:

![Entity Component System as a spreadsheet](https://i.imgur.com/nThdtwx.png)

An Entity Component System can be visualized as a table with columns of components and rows of entities. To operate on a single component, we select its column and look at each cell. To operate on an entity, we selects its row and look at each cell.

## Advantages Of ECS

Now that we have a better understanding of the Entity-Component-System architecture, let's think about how this approach compares to class-based inheritance.

**Good decoupling, single responsibility principle:** each behaviour or domain is decoupled from each other in independent components and/or systems. Unlike our monolithic god object in class based inheritance, we can extract any subset of functionality and assemble it in any combination. ECS also encourages small interfaces.

**Composability and runtime object definition:** Any type of game object can be created by adding the correct components to an entity. This can also allow the developer to easily add features of one type of object to another, without any dependency issues. For example, we can do `Entity.build([FlyingComponent, SeeingComponent])` at runtime.

**Testable**: Each component and system is a unit by definition. We can also substitute components with mocked or demo components for testing.

**Parallelizable**: In many real-world ECS implementations such as MMOs, a System is implemented as a distributed system or a worker pool that can distribute the work amongst themselves. This lets us horizontally scale the size of our simulations by increasing the number of system workers in our pool.

**Separation of data and behaviour**: components hold data, systems hold behaviour. There is no intermingling of the two. This property lets you plug-and-play different behaviour to apply on the same data.

## Challenges Of ECS

Despite the flexibility this gives us, ECS introduces a number of non-trivial challenges:

**ECS is a relatively unknown pattern:** Since this design pattern has been mostly limited to game development, discussing how to use ECS for domains outside of it such as for building web apps can be challenging. There are few resources available for applying this pattern to other domains, if any.

**Handling interprocess communication:** How do we handle the communication between systems and components? We need some kind of message bus or publish-subscribe system to enable parts of our ECS to talk to each other. Depending on the language or platform the ECS is implemented in, this could introduce a spike in complexity. The cost of iterating through components and entities may also result in a drop in performance.

![A message bus](https://engineeringblog.yelp.com/images/posts/2016-07-14-billions-of-messages-a-day-yelps-real-time-data-pipeline/1.jpg)

**Inter-component communication:** What happens when a system needs to access and modify data in multiple components? Components might need to share state with other components and communicate with each other prior to communicating with systems. For example, say we have a `Position` and `Sound` component in an entity. We could have a `PositionalSoundSystem` that needs to communicate with both components. We may need a separate channel for inter-component communication to support this use case.

**Inter-system communication:** What happens when two systems need to access and modify the same component? Say we have two systems: one multiplies attribute `x` by `-1`, the other adds `x` by `10`. Depending on the order of application of the two systems, the end result will be different. Unless the operations are associative, we may need to introduce a way to ensure that the order of the systems are correct.

**Not as concretely define as other design patterns such as MVC:** There are a multitude of ways to implement ECS. Each language or platform will have different abstractions available, which result in different flavours of ECS.

# ECS in the Real world

Aside from being a popular architecture for video games, current applications of ECS are for large-scale distributed simulations. This includes real-time city traffic, internet telecommunications network, and physics simulations. It’s also used for building massively multiplayer backends for video games with staggering numbers of entities.

![ECS in the real world](https://i.imgur.com/bgW0JX4.png)

One startup in particular, is building an ECS-as-a-service called [SpatialOS](https://improbable.io/).

# An ECS implementation in Elixir

In this section, we'll take a look at one possible implementation of Entity-Component-System in Elixir. I'll start by briefly mentioning why Elixir (also Erlang) and its concurrency primitives is a good fit for the ECS pattern.

> From here on out, since Elixir compiles to Erlang bytecode, when I say Elixir I also mean Erlang.

## The Actor Model

One of the key abstractions of Elixir are processes - these are akin to actors of the actor model. Actors are computations entities that can:

- Send messages
- Receive messages
- Spawn new actors

![The actor model](https://i.imgur.com/ANwj1Ak.png)

In the diagram above, Actor `A` sends messages `1` and `2` to Actor `C`, which it receives. In response to these messages, Actor C can do send new messages, or spawn new actors and wait to receive messages from those actors.

```
# Try running this in an Elixir interpreter (iex)

# We first spawn a new actor that listens for messages, returning a process id (pid)
jeff = spawn(fn ->
        receive do
          {sender, message} -> IO.puts "Received '#{message}' from process #{inspect sender}"
        end
       end)

# We send a message to jeff's pid, adding our own pid in the message
send jeff, {self(), “Hello world”}

# send, receive, and spawn are built-in Elixir primitives
```

Elixir also has higher-level abstractions for building actors called `GenServers`:

```
defmodule Stack do
  use GenServer

  # Callbacks

  def handle_call(:pop, _from, [h | t]) do
    {:reply, h, t}
  end

  def handle_cast({:push, item}, state) do
    {:noreply, [item | state]}
  end
end

# Start the server
{:ok, pid} = GenServer.start_link(Stack, [:hello])

# This is the client
GenServer.call(pid, :pop)
#=> :hello

GenServer.cast(pid, {:push, :world})
#=> :ok

GenServer.call(pid, :pop)
#=> :world
```

> Consider how you might implement ECS with the help of actors.

## Usage Example

Here's what our implementation would look like in use:

```
# Instantiates a new entity with a set of parameterized components
# TimeComponent is a component that counts up
> bunny = ECS.Entity.build([TimeComponent.new(%{age: 0})])

# We trigger the TimeSystem to enumerate over all TimeComponents
# In the real world this could be in response to an event stream such as player input
> TimeSystem.process

# We pull the latest state of the components
> bunny = ECS.Entity.reload(bunny)

# We can repeat this process
> TimeSystem.process
> bunny = ECS.Entity.reload(bunny)

# Modifies an existing entity at runtime by adding a new component to it
bunny = ECS.Entity.add(bunny, TimeComponent.new(%{age: 10}))

# We can repeat this process, and both TimeComponents will receive state updates
TimeSystem.process
bunny = ECS.Entity.reload(bunny)
```

My ECS implementation in Elixir is open source on [Github](https://github.com/yosriady/ecs). You can clone and run it via `iex -S mix` from the root folder. You must have Elixir installed on your machine.

# Implementation

## Entity

An entity is a struct with a randoms string `id` and a list of components. We can create entities and extend it by adding components. Both can be done at runtime.

```
defmodule ECS.Entity do
  @moduledoc """
    A base for creating new Entities.
  """

  defstruct [:id, :components]

  @type id :: String.t
  @type components :: list(ECS.Component)
  @type t :: %ECS.Entity{
    id: String.t,
    components: components
  }

  @doc "Creates a new entity"
  @spec build(components) :: t
  def build(components) do
    %ECS.Entity{
      id: ECS.Crypto.random_string(64),
      components: components
    }
  end

  @doc "Add components at runtime"
  def add(%ECS.Entity{ id: id, components: components}, component) do
    %ECS.Entity{
      id: id,
      components: components ++ [component]
    }
  end

  @doc "Pulls the latest component states"
  @spec reload(t) :: t
  def reload(%ECS.Entity{ id: _id, components: components} = entity) do
    updated_components = components
      |> Enum.map(fn %{id: pid} ->
        ECS.Component.get(pid)
      end)

    %{entity | components: updated_components}
  end
end
```

Below is an actual entity, the `Bunny`:

```
# A bunny prefab
defmodule Bunny do
  def new do
    ECS.Entity.build([TimeComponent.new(%{age: 0})])
  end
end
```

> The above code introduces the idea of a 'prefab', which are convenient factories for entities with a common set of components. Using the prefab saves you from typing too much, and acts as a facade.

## Component

The `Component` and `Component.Agent` modules provide facilities to get and set state. Each Component is backed by an Actor (an Agent - a kind of GenServer.) Components such as `TimeComponent` implement the `Component` behaviour (interface.)

```
ddefmodule ECS.Component do
  @moduledoc """
    A base for creating new Components.
  """

  defstruct [:id, :state]

  @type id :: pid()
  @type component_type :: String.t
  @type state :: map()
  @type params :: map()
  @type t :: %ECS.Component{
    id: id, # Component Agent ID
    state: state
  }

  @callback new(state) :: t # Component interface

  defmacro __using__(_options) do
    quote do
      @behaviour ECS.Component # Require Components to implement interface
    end
  end

  @doc "Create a new agent to keep the state"
  @spec new(component_type, state) :: t
  def new(component_type, initial_state) do
    {:ok, pid} = ECS.Component.Agent.start_link(initial_state)
    ECS.Registry.insert(component_type, pid) # Register component for systems to reference
    %{
      id: pid,
      state: initial_state
    }
  end

  @doc "Retrieves state"
  @spec get(id) :: t
  def get(pid) do
    state = ECS.Component.Agent.get(pid)
    %{
      id: pid,
      state: state
    }
  end

  @doc "Updates state"
  @spec update(id, state) :: t
  def update(pid, new_state) do
    ECS.Component.Agent.set(pid, new_state)
    %{
      id: pid,
      state: new_state
    }
  end
end
```

```
defmodule ECS.Component.Agent do
  @moduledoc """
    Create a simple Agent that gets and sets.
    Each component instantiates one to keep state.
  """

  @doc "Starts a new bucket. Returns {:status, pid}"
  def start_link(initial_state \\ %{}, opts \\ []) do
    Agent.start_link((fn -> initial_state end), opts)
  end

  @doc "Gets entire state from pid"
  def get(pid) do
    Agent.get(pid, &(&1))
  end

  @doc "Gets a value from the `pid` by `key`"
  def get(pid, key) do
    Agent.get(pid, &Map.get(&1, key))
  end

  @doc "Overwrites state with new_state."
  def set(pid, new_state) do
    Agent.update(pid, &Map.merge(&1, new_state))
  end

  @doc "Updates the `value` for the given `key` in the `pid`"
  def set(pid, key, value) do
    Agent.update(pid, &Map.put(&1, key, value))
  end
end
```

Below is an actual component, the `TimeComponent`, which implements the `Component` behaviour:

```
defmodule TimeComponent do
  @moduledoc """
    A component for keeping an age for something.
    {id: pid, state: state} = TimeComponent.new(%{age: 1})
  """
  use ECS.Component

  @component_type __MODULE__

  @doc "Initializes and validates state"
  def new(%{age: _age} = initial_state) do
    ECS.Component.new(@component_type, initial_state)
  end
end
```

## System and Registry

Systems enumerates over all components of its type.

```
defmodule TimeSystem do
  @moduledoc """
    Increments ages of TimeComponents
  """

  def process do
    components()
      |> Enum.each(fn (pid) -> dispatch(pid, :increment) end)
  end

  # dispatch() is a pure reducer that takes in a state and an action and returns a new state
  defp dispatch(pid, action) do
    %{id: _pid, state: state} = ECS.Component.get(pid)

    new_state = case action do
      :increment ->
        Map.put(state, :age, state.age + 1)
      :decrement ->
        Map.put(state, :age, state.age - 1)
      _ ->
        state
    end

    IO.puts("Updated #{inspect pid} to #{inspect new_state}")
    ECS.Component.update(pid, new_state)
  end

  defp components do
    ECS.Registry.get(:"Elixir.TimeComponent")
  end
end
```

A few things to notice:

- `dispatch` takes in an external action, evaluates it based on an internal rule, and returns a new state. This part was inspired largely by my experiences with Redux reducers and Elm's `update`.
- The `components` method returns the set of components that this System will enumerate. Whenever a Component is instantiated, it registers its agent to the `Registry` which keeps track of all active components. The Registry is itself an actor, shown below:

```
defmodule ECS.Registry do
  @moduledoc """
    Component registry.
    iex> {:ok, r} = ECS.Registry.start
    iex> ECS.Registry.insert("test", r)
    :ok
    iex> ECS.Registry.get("test")
    [#PID<0.87.0>]
  """

  def start do
    Agent.start_link(fn -> %{} end, name: __MODULE__)
  end

  def insert(component_type, component_pid) do
    Agent.update(__MODULE__, fn(registry) ->
      components = (Map.get(registry, component_type, []) ++ [component_pid])
      Map.put(registry, component_type, components)
    end)
  end

  def get(component_type) do
    Agent.get(__MODULE__, fn(registry) ->
      Map.get(registry, component_type, [])
    end)
  end
end
```

And that's it!

> This particular ECS implementation may be a bit rough around the edges. There are many flavours of ECS, and this is most certainly not the only way to make ECS work. Your feedback is most welcome!

## In closing

ECS is an overlooked architectural pattern that overcomes some of the drawbacks of OOP-style inheritance, and is a great fit for distributed systems.

Branching out into unfamiliar domains (such as game development) is a fruitful source of new ideas and patterns to write better software.

Thanks for reading! I hope you found this article useful or otherwise interesting. Let me know your thoughts via the comments below!