Skip to content

5. Component and System Design

Knowlife4 edited this page Mar 10, 2023 · 3 revisions

Validations

Validations are an integral part to creating any component as they allow you to validate before executing.

Validation Types

There are 2 built-in validation types in ECT:

  • ECTBoolValidation

    Used to validate if a bool is true.

  • ECTReferenceValidation

    Used to validate if a Unity Object is not null.

Custom Validations

ECT has support for creating custom validations simply by deriving from the IValidation interface.

Important Validation Methods

The static class ECTValidation is used to access useful validation helper methods.

ECTValidation contains the following methods:

  • ValidateReference(Object)

    This method is used to validate a Unity Object reference.

  • ValidateReference(Object, out Object)

    This method is used to validate a Unity Object reference while outputting the valid result.

  • ValidateReferences(params Object[])

    This method is used to validate several Unity Object references.

  • ValidateMany(params IValidation[])

    This method is used to validate a set of validations.

Note: ValidateReference(Object, out Object) and ValidateReferences(params Object[]) are accessible from within a component.


Validation

Components

Components are responsible for the following:

  • Serialized Data
  • Component Systems
  • Scene References

Serialized Data

Creating a field for serialized data is extremely simple.

  • Create a field for your data and mark it with the [SerializeField] attribute.

    Ex. [SerializeField] float speed

Component Systems

Components have "Component Systems", or Systems that are tied to that component.

To create a Component System, create a public subclass and derive it from System<> and populate the generic with the component type.

Note: It is recommended to name your Component Systems variations of System to make referencing them easier.

Ex. Creating a Component System for PlayerMovement would look like this: public class System : System<PlayerMovement>.

Defining Component Systems

  • [ComponentSystem] Attribute

    A fast and easy way to define a Component System is to mark a system with the [ComponentSystem] attribute.

    Note: Only a single system can be defined at once using this attribute.

  • Override the GetSystem() method

    For more complex components that may require more than one system, you can define them by overriding the GetSystem() method.

    Ex. protected override GetSystem() => new System()

Scene References

Components can optionally contain a single SceneReference, which allows you to reference GameObjects in the Scene.

Creating a SceneReference

  • Create a public struct and derive it from ISceneReference.

    Ex. public SceneReference : ISceneReference

  • Tag it with the [SceneReference] attribute so it can be displayed in the Entity Editor.

    Note: Components only support a single SceneReference at this time, though this may be changed later.

  • Create public fields of the types you wish to reference.

    Ex. public Camera Camera and public Transform CameraTarget

    Note: You should only reference types that derive from UnityEngine.Object.

Validating a SceneReference

  • Implement the required Validation property and validate each reference using the ValidateReferences(params Object[]) method.

    Ex. public IValidation Validation => ValidateReferences(Camera, CameraTarget)


Scene Reference | Validation

Example Component

public class PlayerMovement : PlayerEntity.Component
{
    [SerializeField] float speed;

    [SceneReference]
    public struct SceneReference : ISceneReference
    {
        public Camera Camera;
        public Transform CameraTarget;
        
        public IValidation Validation => ValidateReferences(Camera, CameraTarget);
    }

    [ComponentSystem]
    public class System : System<PlayerMovement>
    {
        protected override void OnUpdate() { }
    }
}

Component Parents

Components can optionally be made into Component Parents, which allows them to contain their own ComponentGroup.

Creating a Component Parent

Creating a Component Parent is the same as creating a traditional Component except you derive from Component.Parent instead.

Ex. Instead of PlayerEntity.Component you derive from PlayerEntity.Component.Parent.

Component Parents will execute all child component's systems in their System's OnUpdate(), but this can be changed by overriding it.

To update these component's systems manually, you can call the UpdateSystems() method.

Note: Creating sub-components for your component parent is exactly the same as creating a component for an entity.

Systems

Systems are responsible for the following:

  • Non-Serialized Data
  • Referencing SystemData
  • Validating Dependencies
  • Component Logic

Non-Serialized Data

Creating non-serialized data is as simple as creating ordinary fields as systems are created at runtime.

Ex. Vector3 velocity

Referencing SystemData

SystemData allows your system to access the following:

  • Entity with Root

  • Parent with Parent

  • Component with Component

Validating Dependencies

Systems can define Validations that have to succeed for them to be executed.

This is done by overriding the Validations property and defining the validations you wish to execute.

Ex. protected override IValidation[] Validations => new[]{ ValidateReference(Root.CharacterController, out controller) }

Pulling in other Systems or SceneReferences

Within a system, you have access to the following methods:

  • QuerySystem(out ISystem)

  • QueryReference(out ISceneReference)

These allow you to find, validate, and pull in either a System or SceneReference.


Validation

Component Logic

To make your components actually do something, you need to tell the system how to modify the available data.

Introduction Points

Systems have the following methods to execute logic from:

  • OnInitialize()

    Executes when the system is first created.

    Note: Think of OnInitialize() as the equivalent of the Awake() method for MonoBehaviours.

  • OnUpdate()

    Executes when called by the system's parent.

    Note: Think of OnUpdate() as the equivalent of the Update() method for MonoBehaviours.

Example System

public class System : System<PlayerMovement>
{
    Vector3 velocity;

    CharacterController controller;
    
    protected override IValidation[] Validations => new[]
    {
        ValidateReference(Root.CharacterController, out controller)
    }

    protected override void OnUpdate()
    {
        controller.Move(velocity * Component.velocityIntensity);
    }
}

System