Last active
January 11, 2017 10:10
-
-
Save yosriady/c8e1afcbbddfce52d776634d8ff2947f to your computer and use it in GitHub Desktop.
Entity Component System in Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A component is a minimal data object needed for a specific purpose. | |
# A component has no behaviour. | |
# A component class validates a given configuration and creates run-time data structures. | |
# Implemented as an Elixir Agent! | |
defmodule Component | |
@type options :: [key: type] | |
# Functions that have to be implemented by Components (if any) | |
@callback new(options) :: struct() | |
defmacro __using__(_options) do | |
quote do | |
@behaviour Component | |
# Built-in functions inherited by all Components | |
def type, do: __MODULE__ end # Does this work? Supposed to return the type of the component | |
# Overridable built-in functions | |
defoverridable [new: 1] | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# An entity is a container of components. An entity is solely the sum of its components. | |
# An entity is a unique, autogenerated identifier and a list of components with specific configurations. | |
# An entity is nothing without its components. | |
# Entities themselves store only metadata, contain no state, and have no behaviour. | |
# An entity's components gives the entity state and systems give entities behaviour (through components.) | |
defmodule Entity | |
defstruct [:id, :components] | |
@type id :: String.t | |
@type components :: list() | |
@type t :: %Entity{ | |
id: String.t, | |
components: components | |
} | |
@spec new(components, id) :: t | |
def new(components, id \\ random_string(64)) do | |
// TODO: Need to instantiate component agents and | |
// register their names in some process registry | |
%Entity{ | |
id: id, | |
components: components | |
} | |
end | |
defp random_string(length) do | |
:crypto.strong_rand_bytes(length) |> Base.url_encode64 |> binary_part(0, length) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A concrete implementation of a Component. | |
defmodule PositionComponent do | |
use Component | |
defstruct [:x, :y] | |
@type x :: integer | |
@type y :: integer | |
@type t :: %Position{ | |
x: x, | |
y: y | |
} | |
@type options :: [key: type] | |
@type new(options) :: t | |
def new(options) do | |
%Position{ | |
x: options[:x] || 0, | |
y: options[:y] || 0 | |
} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Systems are the only place where behaviour is kept. | |
# Implemented as an Elixir GenServer! | |
defmodule System | |
defstruct [:component_types, :options] | |
@type component_type :: String.t | |
@type component_types :: list(component_type) | |
@type component_state :: struct() | |
@type component_states :: list(component_state) | |
# Functions that have to be implemented by Systems | |
@doc """ | |
`new` instantiates a system (GenServer) with given configuration | |
""" | |
@callback new(options) :: pid | |
@doc """ | |
`update` iterates over all components it applies to | |
and return the new states for those components. | |
""" | |
@callback update(component_states) :: component_states | |
defmacro __using__(_options) do | |
quote do | |
use GenServer # http://elixir-lang.org/docs/stable/elixir/GenServer.html | |
@behaviour System | |
# Built-in functions inherited by all Systems | |
def start, do: :TODO # Turn on system GenServer | |
def stop, do: :TODO # Turn off system Genserver | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A concrete implementation of a Systsem. | |
defmodule WindSystem | |
use System | |
def new(options) do | |
# TODO | |
end | |
def update(component_states) do | |
# TODO | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Example world defined using ECS | |
iex> bird = Entity.new([Position.new(10, 10)]) | |
iex> bird.id | |
12sadf313dfsasfd08 | |
iex> bird.position | |
{x: 10, y: 10} | |
iex> wind_system = WindSystem.new(delta_x: 1, delta_y: 2) | |
iex> wind_system.start | |
iex> wind_system.update | |
iex> bird.position | |
{x: 11, y: 12} | |
iex> wind)system.update | |
iex> bird.position | |
{x: 12, y: 14} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment