Skip to content

Component

Magicolo edited this page Jan 31, 2019 · 26 revisions

Content


Description

A component is a block of data that groups a set of properties that belong together such that they can be associated with an entity. They are meant to be simple, predictable, serializable and inert, and as such, they should never hold any logic (meaning no methods, properties, contructors, events, etc.). If you need helper logic to create the component or to process it, use utility or extension functions that are defined externally to the component.

A component must:

  • be a struct
  • implement the IComponent empty interface

A component should:

  • contain only public instance non-readonly fields (no methods, constructors, properties, events, etc.)
  • not have fields that stores an Entia type (such as a component, a system, a message, a queryable, an injectable, etc.) except for an entity

Usage

using Entia;
using Entia.Injectables;
using Entia.Queryables;
using Entia.Systems;
using Utilities;

namespace Components
{
    public struct Position : IComponent { public float X, Y, Z; }
    public struct Velocity : IComponent { public float X, Y, Z; }

    public struct Target : IComponent
    {
        // An Entity field can be used to link data together.
        public Entity Entity;
    }

    // Empty components can be used to tag an entity.
    public struct IsFrozen : IComponent { }
}

namespace Utilities
{
    // Logic related to 'Components.Position' is implemented via an external 
    // utility class.
    // This keeps the component definition as minimal as possible.
    public static class Position
    {
        public static Components.Position Create(float x, float y, float z = 0f) =>
            new Components.Position { X = x, Y = y, Z = z };

        // The 'ref' keyword is essential here since the 'Position' struct is 
        // being mutated.
        // Without it, a copy of the component would be modified, which would 
        // result in no effect.
        public static void Add(
            ref this Components.Position position,
            in Components.Velocity velocity)
        {
            position.X += velocity.X;
            position.Y += velocity.Y;
            position.Z += velocity.Z;
        }
    }
}

namespace Systems
{
    public struct Motion : IRun
    {
        // This attribute will filter out all entities that have 
        // the 'IsFrozen' component.
        [None(typeof(Components.IsFrozen))]
        public readonly struct Query : IQueryable
        {
            public readonly Entity Entity;
            // This queryable indicates the intention to write to 
            // 'Components.Position'.
            public readonly Write<Components.Position> Position;
            // This queryable indicates the intention to read from 
            // 'Components.Velocity'.
            public readonly Read<Components.Velocity> Velocity;
        }

        // This injectable gives access to all operations related to 
        // 'Components.Position'.
        public readonly Components<Components.Position> Positions;
        // This injectable gives access only to read operations related to 
        // 'Components.Velocity'.
        public readonly Components<Components.Velocity>.Read Velocities;
        // This injectable gives access to all component operations.
        public readonly AllComponents Components;

        // This group will contain entities that have both a 
        // 'Components.Position' and a 'Components.Velocity'.
        public readonly Group<Query> Group;

        public void Run()
        {
            foreach (ref readonly var item in Group)
            {
                // Unpack the group item.
                var entity = item.Entity;
                ref var position = ref item.Position.Value;
                ref readonly var velocity = ref item.Velocity.Value;

                // This uses the extension defined in 'Utilities.Position'.
                position.Add(velocity);

                // Removes the position component from the entity.
                Positions.Remove(entity);
                // Sets or add a new position component to the entity using 
                // the utility function in 'Utilities.Position'.
                Positions.Set(entity, Position.Create(100f, 100f));
                // 'Velocities' has only access to read operations such as 
                // 'Has' and 'Get'.
                var has = Velocities.Has(entity);
            }
        }
    }
}

Related