Skip to content
This repository has been archived by the owner on Dec 20, 2022. It is now read-only.

Systems and Constraints

Gauthier Billot edited this page Jun 28, 2017 · 2 revisions

Systems

Systems contain your game's logic, previously written in a MonoBehavior's Start() or Update() methods.

Systems SHOULD NEVER store any data, i.e. no variables or constants. They can only have Start(), Update(), FixedUpdate(), Event Handler methods, and helper methods.

To create a System, create a new class deriving from EgoSystem:

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem
{
    //...
}

Start(), Update() and FixedUpdate()

Like with typical Unity3D Components, you can write initialization code in Start(), update your GameObjects in Update(), and update physics objects in FixedUpdate():

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem
{
    public override void Start()
    {	
        // ...
    }	

    public override void Update()
    {
        // ...
    }
	
    public override void FixedUpdate()
    {
        // ...
    }
}

Unlike regular MonoBehaviors, you need to override Start(), Update() and FixedUpdate(). This helps to prevent the dreaded "I wrote update() instead of Update()" bug.

Constraints

Systems can update any and all GameObjects with the desired Components through its constraint member. You define the Constraint with the Type Parameter of an EgoSystem:

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem<
    EgoConstraint< ... >
>{
    //EgoConstraint< ... > constraint;
}

You then set the desired Components for a Constraint in an EgoConstraint's Type Parameters:

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem<
    EgoConstraint< Transform, Rigidbody, Example >
>{
    //EgoConstraint< Transform, Rigidbody, Example > constraint; 
}

Here, ExampleSystem's Constraint selects any and all GameObjects with a Transform, Rigidbody, and Example Component.

EgoConstraints automatically cache the attached EgoComponent and desired Components for every relevant GameObject. EgoConstraints by default support up to 12 Components.

ForEachGameObject()

Constraints have a ForEachGameObject() method. It lets Systems iterate over each relevant GameObject (in no guaranteed order):

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem<
    EgoConstraint<Transform, Rigidbody, Example>
>{
    public override void Update()
    {
        constraint.ForEachGameObject(...);
    }
}

ForEachGameObject() has one void callback argument, which is invoked for each relevant GameObject. The callback's first argument must be an EgoComponent, and the following arguments must match the Constraint's desired Components. You can pass in a method reference, but passing in a lambda is the suggested style:

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem<
    EgoConstraint<Transform, Rigidbody, Example>
>{
    public override void Update()
    {
        constraint.ForEachGameObject( ( egoComponent, transform, rigidbody, example ) =>
        {
            //...   
        } );
    }
}

You can safely call ForEachGameObject() anywhere in a System: Start(), Update(), FixedUpdate(), Event Handler methods, and Helper methods.

NOTE: It is a severe anti-pattern to nest ForEachGameObject() calls on the same constraint.

Parent Constraints

An EgoParentConstraint is an extension of a regular EgoConstraint that defines what Components a GameObject's parent must have. You set the parent GameObject's desired Components as the EgoParentConstraint initial Type Parameters:

EgoParentConstraint< Transform, Rigidbody, ParentExample, ... >

The final Type Parameter of an EgoParentConstraint must be a fully defined child Constraint:

EgoParentConstraint< ParentExample,
    EgoConstraint< ChildExample >
>

Above, these Constraints only select GameObjects with ChildExample Components, and whose parent GameObject (where it exists) has a ParentExample Component. If the child nor parent meet these requirements, the Constraints will ignore them.

You can nest EgoParentConstraints indefinitely, but the final innermost Constraint must be an EgoConstraint:

EgoParentConstraint< ... ,
    EgoParentConstraint< ... ,
        EgoParentConstraint< ... ,
            EgoConstraint< ... >
        >
    >
>

When calling ForEachGameObject() on an EgoParentConstraint, the callback must accept the parent's EgoComponent, the parent's desired Components, and the child constraint:

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem<
    EgoParentConstraint< Transform, 
        EgoConstraint< Transform >
    >
>{
    public override void Update()
    {
        constraint.ForEachGameObject( ( egoComponent, transform, childConstraint ) =>
        {
            //...
        } );
    }
}

You can then call ForEachGameObject() on the childConstraint:

// ExampleSystem.cs
using UnityEngine;

public class ExampleSystem : EgoSystem<
    EgoParentConstraint< Transform, 
        EgoConstraint< Transform >
    >
>{
    public override void Update()
    {
        constraint.ForEachGameObject( ( egoComponent, transform, childConstraint ) =>
        {
            childConstraint.ForEachGameObject( ( childEgoComponent, childTransform ) =>
            {
                //...
            } );
        } );
    }
}

NOTE: Use EgoParentConstraints, and especially nested EgoParentConstraints, sparingly. They are best used when describing a hierarchy of GameObjects is necessary, such as UI. If a System's logic can be adaquetely expressed with a simple EgoConstraint, please do so.