View Source Ecspanse.Component behaviour (ECSpanse v0.10.0)

The Ecspanse.Component is the basic building block of the ECS architecture, holding the entity state. The components are defined by invoking use Ecspanse.Component in their module definition.

An entity cannot exist without at least a component. And the other way around, a component cannot exist without being allocated to an entity. The components hold their own state, and can also be tagged for easy grouping.

There are two ways of providing the components with their initial state and tags:

  1. At compile time, when invoking the use Ecspanse.Component, by providing the :state and :tags options.

    defmodule Demo.Components.Position do
     use Ecspanse.Component, state: [x: 3, y: 5], tags: [:map]
    end
  2. At runtime when creating the components from specs: t:Ecspanse.Component.component_spec():

    Ecspanse.Command.spawn_entity!({Ecspanse.Entity,
     components: [
       Hero,
       {Demo.Components.Position, [x: 7, y: 2], [:map]},
     ]
    )
    
    # or
    
    Ecspanse.Command.add_component!(hero_entity, {Demo.Components.Position, [x: 7, y: 2], [:map]})

After being created, components become structs with the provided fields, along with some metadata added by the framework. Components can also be used as an entity label, without state.

There are some special components that are created automatically by the framework:

Options

  • :state - a list with all the component state struct keys and their initial values (if any). For example: [:amount, max_amount: 100]
  • :tags - list of atoms that act as tags for the current component. Defaults to [].
  • :export_filter - :none | :component | :entity - indicates if the component should be exported. Defaults to :none. See Ecspanse.Snapshot for details.

    • :none - no filter. The component will be exported.
    • :component - the component will not be exported.
    • :entity - the whole entity containing the component will not be exported.

Tags

Tags can be added at compile time, and at runtime only when creating a new component. They cannot be edited or removed later on for the existing component.

The List of tags added at compile time is merged with the one provided at run time.

Summary

Implemented Callbacks

Fetches the component for an entity. It has the same functionality as Ecspanse.Query.fetch_component/2, but it may be more convenient to use in some cases.

Lists all components of the current type for all entities.

Types

A component_spec is the definition required to create a component.

Callbacks

Optional callback to validate the component state. It takes the component state struct as the only argument and returns :ok or an error tuple.

Functions

Utility function. Returns all the components and their state, together with their entity association and tags.

Implemented Callbacks

@callback fetch(entity :: Ecspanse.Entity.t()) ::
  {:ok, component :: struct()} | {:error, :not_found}

Fetches the component for an entity. It has the same functionality as Ecspanse.Query.fetch_component/2, but it may be more convenient to use in some cases.

Implemented Callback

This callback is implemented by the library and can be used as such.

Examples:

    {:ok, %Demo.Components.Position{} = position_component} = Demo.Components.Position.fetch(hero_entity)

    # it's the same as:

    {:ok, %Demo.Components.Position{} = position_component} = Ecspanse.Query.fetch_component(hero_entity, Demo.Components.Position)
@callback list() :: [component :: struct()]

Lists all components of the current type for all entities.

Implemented Callback

This callback is implemented by the library and can be used as such.

Examples:

  enemy_components = Demo.Components.Enemy.list()

Types

@type component_spec() ::
  (component_module :: module())
  | {component_module :: module(), initial_state :: keyword()}
  | {component_module :: module(), initial_state :: keyword(), tags :: [atom()]}

A component_spec is the definition required to create a component.

Examples

  Demo.Components.Gold
  {Demo.Components.Gold, [amount: 5]}
  {Demo.Components.Gold, [amount: 5], [:resource, :available]}
  {Demo.Components.Gold, [], [:resource, :available]}

Callbacks

Link to this callback

validate(component)

View Source (optional)
@callback validate(component :: struct()) :: :ok | {:error, any()}

Optional callback to validate the component state. It takes the component state struct as the only argument and returns :ok or an error tuple.

Info

When an error tuple is returned, it raises an exception with the provided error message.

Note

For more complex validations, Ecto schemaless changesets may be useful.

Examples

  defmodule Demo.Components.Gold do
    use Ecspanse.Component, state: [amount: 0]

    def validate(%__MODULE__{amount: amount}) do
      if amount >= 0 do
        :ok
      else
        {:error, "Gold amount cannot be negative"}
      end
    end
  end

Functions

@spec debug() :: [
  {{Ecspanse.Entity.id(), component_module :: module()}, tags :: [atom()],
   component_state :: struct()}
]

Utility function. Returns all the components and their state, together with their entity association and tags.

This function is intended for use only in testing and development environments.