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:
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
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:
Ecspanse.Component.Children
- holds the list of child entities.Ecspanse.Component.Parents
- holds the parent entities.
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
. SeeEcspanse.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.
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
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.