Concepts - GoldhawkInteractive/X2-Modding GitHub Wiki

Besides the three big ones (Entity, Component, System), the following concepts in Artitas are important:

  • World: An isolated container for Entities, Components, Systems which can queue and process Events.
  • Family: A set of entities with similar sets of Components, based on a Matcher and multiple Filters.
    • Matcher: Determines whether an Entity may join a Family based on whether it has or doesn't have certain Components.
    • Filter: Determines whether an Entity may join a Family, usually based on the value of a Component.
  • Template: A set of components which can be instantiated in a World as an Entity.
  • Archetype: Represents a type of entity that can occur in your project. Used to create factory methods that allow easy creation and default values.
  • Relationships: Relationships between entities and other entities or concepts.

World

The World is the root entry point into the World of Artitas. It oversees the lifecycle of all concepts in Artitas and ensures that any queued events are routed and handled correctly. The actual logic of these lifecycles is delegated to four Managers which are contained within the World:

  • EntityManager: Manages the Entity lifecycle, both creating and pooling of deleted entities.
  • ComponentManager: Manages the storage and lookup of Components.
  • FamilyManager: Manages the lifecycle of Families and ensures that when an Entity adds or removes a Component it is registered to the appropriate Families.
  • RelationshipManager: Manages the lifecycle of Relationships, and ensures that any changes propogate correctly according to the relationship semantics.

Family

The Family is the primary means of accessing entities. It allows you to group entities based on their Components and react to changes on them. It is supposed to be the first place to look if you want to get an Entity, or entities, with specific sets of Components from the World. It is high-performance and will kept up to date by the world.

The easiest way to define a Family is in the Initialize() of a System. It will ensure that the Family is properly registered in the World lifecycle without any issues and ensure that you don't litter them all over your codebase. For the case where only a single Family is needed, the EntitySystem exists which provides you with a backing Family without any hassle.

// A family that will have all Entities with a Health below or at zero.
public class DeadSystem : EntitySystem {

    public DeadSystem () : base(Matcher.All<Health>()){
        Family.AddFilter((Health h) => h.value <= 0);
    }

}

// Or if you need multiple Families
public class DeadOrALiveSystem : BaseSystem {

     public Family _dead, _alive;

     public void Initialize(World world){
         base.Initialize(world);
         _dead = new Family(Matcher.All<Health>())
                     .AddFilter((Health h) => h.value <= 0);
         _alive = new Family(Matcher.All<Health>())
                     .AddFilter((Health h) => h.value > 0);

         world.RegisterFamily(_dead);
         world.RegisterFamily(_alive);
     }

}

A Family is built up around a Matcher, discussed in the following section:

Matcher

A Matcher determines whether an Entity may enter a Family based on which Components it possesses. It can be created with any combination of the following predicates:

  • All: The entity needs to have all specified components.
  • Any: The entity needs to have at least one of the specified components.
  • None: The entity may not have any of the specified components.

Let's explore these concepts through some examples. In our first example, we have a simple 2D rendering system that given a 2D Position and a Texture, will render it on the screen. However, we don't want 3D models to be displayed. So, any entity for this System must have a Position and Texture component, but may not have a Model component. The Matcher for this can be defined by:

Matcher twoDRendering = Matcher.All<Position, Texture>()
                               .None<Model>();

In our second example, we have a rendering system that requires a Texture component and either a Position or a Location component. If the rendering system is fed an entity with a Location it will translate this into a 2D position for rendering, any entities added with Positions are directly rendered. The Matcher for this can be defined by:

Matcher complexRendering = Matcher.All<Texture>()
                                  .Any<Position, Location>()

In our third example, we have a quest system that will only trigger once the player has finished an impossible Quest, has met either the Monk or the Dragon, but has not yet killed the Boss. While traditionally we would not model this through components, it is still possible, and done through the following Matcher:

Matcher questSystem = Matcher.All<QuestFinished>()
                             .Any<MetMonk, MetDragon>()
                             .None<KilledBoss>()

// or spread out, order doesn't matter!
questSystem = Matcher.None<KilledBoss>()
                     .Any<MetMonk>()
                     .All<QuestFinished>()
                     .Any<MetDragon>()
Filter

A Filter is a predicate on an Entity. Shorthand is provided to create Filters based on Components, which allows you to filter based on the value of a Component. This allows you to define Families which can react to certain circumstances on Entities. An example of this is a Family which only responds to "dead" entities:

/ A family with all alive entities
Family alive = world.RegisterFamily(new Family(Matcher.All<Health>())
                                        .AddFilter((Health health) => health.value > 0f));

// A family with all dead entities
Family dead = world.RegisterFamily(new Family(Matcher.All<Health>())
                                       .AddFilter((Health health) => health.value <= 0f));

Template

The Template is your ticket into serialization, copying and performant batch creation of Entities with certain Components. The primary use case for batch creation of Entities is if you need a lot of similar Entities instantiated fast, per example for a particle system.

A Template supports hierarchy by allowing a parent Template to be set. Any Components which don't exist in the child Template will be added to the instantiated Entity. This allows you to define base templates which act similarly to hierarchy in Object Oriented programming.

An example:

// Ensure that anything created for rendering has a model and a position
Template renderTemplate = new Template()
                               .Add(new Model3D(box))
                               .Add(new Position(0,0));

// Ensure that units spawn at 10,10 and have a base health of 50.
Template unitTemplate = new Template{ Parent = renderTemplate }
                         .Add(new Health(50))
                         .Add(new Position(10,10));

// Override the base health of a unit, 3d model and add the entity to the AliensGroup
Template alienTemplate = new Template{ Parent = unitTemplate }
                         .Add(new Model3D(alienModel))
                         .Add(new Health(100)  // Overrides the Health in the parent template
                         .Add(new AliensGroup());

Entity alien = alienTemplate.Create(world);
// Alien is now an Entity with: Model3D(alienModel), Health(100), Position(0,0) and AliensGroup() components.
⚠️ **GitHub.com Fallback** ⚠️