-
Notifications
You must be signed in to change notification settings - Fork 1
5. Component and System Design
Validations are an integral part to creating any component as they allow you to validate before executing.
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.
ECT has support for creating custom validations simply by deriving from the IValidation
interface.
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)
andValidateReferences(params Object[])
are accessible from within a component.
Components are responsible for the following:
- Serialized Data
- Component Systems
- Scene References
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
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>
.
-
[ComponentSystem]
AttributeA 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()
methodFor 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()
Components can optionally contain a single SceneReference, which allows you to reference GameObjects in the Scene.
-
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
andpublic Transform CameraTarget
Note: You should only reference types that derive from
UnityEngine.Object
.
- Implement the required
Validation
property and validate each reference using theValidateReferences(params Object[])
method.Ex.
public IValidation Validation => ValidateReferences(Camera, CameraTarget)
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() { }
}
}
Components can optionally be made into Component Parents, which allows them to contain their own ComponentGroup.
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 fromPlayerEntity.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 are responsible for the following:
- Non-Serialized Data
- Referencing SystemData
- Validating Dependencies
- Component Logic
Creating non-serialized data is as simple as creating ordinary fields as systems are created at runtime.
Ex.
Vector3 velocity
SystemData allows your system to access the following:
-
Entity with
Root
-
Parent with
Parent
-
Component with
Component
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) }
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.
To make your components actually do something, you need to tell the system how to modify the available data.
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 theAwake()
method for MonoBehaviours. -
OnUpdate()
Executes when called by the system's parent.
Note: Think of
OnUpdate()
as the equivalent of theUpdate()
method for MonoBehaviours.
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);
}
}