ECSComb

ECSComb is an Entity-Component-System framework for Elixir built on BEAM and ETS. It focuses on a small runtime surface:

Design Highlights

Quick Example

defmodule Demo.Position do
  defstruct [:x, :y]
end

defmodule Demo.Velocity do
  defstruct [:dx, :dy]
end

defmodule Demo.MoveSystem do
  @behaviour ECSComb.System

  alias ECSComb.Query
  alias ECSComb.World
  alias Demo.Position
  alias Demo.Velocity

  @impl true
  def access do
    %{reads: [Position, Velocity], writes: [Position]}
  end

  @impl true
  def run(world) do
    Query.select(world, all: [Position, Velocity])
    |> Enum.each(fn entity_id ->
      {:ok, %Position{x: x, y: y}} = World.get(world, entity_id, Position)
      {:ok, %Velocity{dx: dx, dy: dy}} = World.get(world, entity_id, Velocity)
      :ok = World.put(world, entity_id, %Position{x: x + dx, y: y + dy})
    end)

    :ok
  end
end

{:ok, world} = ECSComb.World.start_link()
{:ok, entity_id} = ECSComb.World.spawn_entity(world)

:ok = ECSComb.World.put(world, entity_id, %Demo.Position{x: 0, y: 0})
:ok = ECSComb.World.put(world, entity_id, %Demo.Velocity{dx: 1, dy: 1})

scheduler =
  ECSComb.Scheduler.new()
  |> ECSComb.Scheduler.add_system(Demo.MoveSystem)
  |> ECSComb.Scheduler.build_graph()

{:ok, tick_loop} =
  ECSComb.TickLoop.start_link(
    world: world,
    scheduler: scheduler,
    tick_rate: 20
  )

Process.sleep(120)
{:ok, %Demo.Position{x: x, y: y}} = ECSComb.World.get(world, entity_id, Demo.Position)
IO.inspect({x, y}, label: "updated position")

:ok = ECSComb.TickLoop.stop(tick_loop)

Test Coverage

The project includes tests for:

Status

Implemented and covered by mix test.