Unity ECS programming

        Unity ECS (Entity Component System) is a high-performance programming paradigm in the Unity engine. The ECS programming paradigm consists of three core elements: Entity, Component and System. By separating data and logic from each other, Unity ECS makes it easier for developers to develop high-performance games and applications.

<!--more-->

### 1. Entity Entity

        In the ECS programming paradigm, entities represent the basic objects in the game.

        A Unity ECS entity is an untyped object that has only a unique identifier and an optional name.

        In Unity ECS, you can use the Entity type to represent entities.

Entity entity = EntityManager.CreateEntity();

        The above code creates a new entity.

### 2. Component Component

        A component is a unit module, an attribute owned by an entity.

        In Unity ECS, you can use the ComponentDataWrapper type to define components.

        For example, the following code defines a simple component:

public struct MoveSpeed : IComponentData
{
    public int speed;
}

        A structure called MoveSpeed ​​is defined, which implements the IComponentData interface.

        In this component, there is only one attribute speed of integer type.

### 3. System System

        The system is responsible for handling entities and components.

        In Unity ECS, you can use the ComponentSystem type to represent a system.

        Using the system, you can filter and manipulate entities and components.

        The following code injects and removes components for entities:

public class ExampleSystem : ComponentSystem
{
    protected override void OnUpdate()
    {
        Entities.ForEach((ref MoveSpeed moveSpeed) =>
        {
            moveSpeed.speed++;
            if (moveSpeed.speed > 10)
            {
                EntityManager.RemoveComponent<MoveSpeed>(entity);
            }
        });
    }
}

        The OnUpdate() method in the system will be called once every frame. In this method, components can be filtered and manipulated.

        The Entities.ForEach() method in the above code is used to traverse all entities whose component type is MoveSpeed.

        Here is a new structure for the MoveSpeed ​​component:

public struct MoveSpeed : IComponentData
{
    public float speed;
}

        The component now includes a property speed of type float.

        Now you can attach the MoveSpeed ​​component to the entity:

Entity entity = EntityManager.CreateEntity(typeof(MoveSpeed));
EntityManager.SetComponentData(entity, new MoveSpeed { speed = 5.0f });

        The above code attaches a new MoveSpeed ​​component to the entity, setting the value of the speed property to 5.0.

        For the convenience of demonstration, the following code snippets are used for encapsulation:

public Entity CreateEntityWithMoveSpeed(float initialSpeed = 0.0f)
{
   Entity entity = EntityManager.CreateEntity(typeof(MoveSpeed));
   EntityManager.SetComponentData(entity, new MoveSpeed { speed = initialSpeed });
   return entity;
}

        This approach will help provide a uniform interface for all entities that attach MoveSpeed ​​components. Now execute the method:

Entity entity = CreateEntityWithMoveSpeed(5.0f);
EntityManager.SetComponentData(entity, new MoveSpeed { speed = 7.5f });

        will result in an entity with a velocity of 7.5. You can also find the MoveSpeed ​​component with the following code:

public class MoveSpeedSystem : ComponentSystem
{
    protected override void OnUpdate()
    {
        Entities.ForEach((Entity entity, ref MoveSpeed moveSpeed) =>
        {
            Debug.Log($"Entity {entity} has MoveSpeed {moveSpeed.speed}");
        });
    }
}

        The above code will print all entities that have a MoveSpeed ​​component attached to them.

### 4. Entities and components

        A component instance is a property of an entity. An entity can have multiple properties, and each property must be of a different type. You are free to add and remove these attributes for each entity.

        Since each entity can have multiple components, the system can filter by component type to find entities of a specific type.

        The following code creates two entities and attaches some components to them:

Entity entity1 = CreateEntityWithMoveSpeed(5.0f);
Entity entity2 = CreateEntityWithMoveSpeed(10.0f);

EntityManager.AddComponentData(entity1, new RotationSpeed { speed = 2.0f });
EntityManager.AddComponentData(entity2, new RotationSpeed { speed = 4.0f });

EntityManager.AddComponentData(entity1, new Position { x = 0.0f, y = 0.0f, z = 0.0f });
EntityManager.AddComponentData(entity2, new Position { x = 10.0f, y = 0.0f, z = 0.0f });

EntityManager.AddComponentData(entity1, new Scale { x = 1.0f, y = 1.0f, z = 1.0f });
EntityManager.AddComponentData(entity2, new Scale { x = 2.0f, y = 2.0f, z = 2.0f });

        The above code creates two entities and attaches four different components MoveSpeed, RotationSpeed, Position and Scale to them.

### 5. Query expression

        You can use query expressions to find entities with different components.

        The following code demonstrates how to use a query expression to find all entities with a RotationSpeed ​​component:

[Unity.Burst.BurstCompile]
public struct RotationSpeedJob : IJobForEachWithEntity<RotationSpeed>
{
    public void Execute(Entity entity, int index, [ReadOnly]ref RotationSpeed rotation)
    {
        float deltaTime = Time.deltaTime;
        float3 euler = new float3(0, rotation.speed * deltaTime, 0);
        EntityManager.SetComponentData(entity, new Rotation { Value = quaternion.Euler(euler) });
    }
}

        The above code specifies which type of entity to search for through its generic type parameter.

        You can manipulate the system by implementing the .Execute() method. In the demo, the .Execute() method processes all of the entity's components each frame.

### 6. Aggregation component - IComponentData

        An aggregate component is a component that contains a set of data that can be shared across different systems.

        The following code demonstrates how to create a component that contains an array of data:

using Unity.Entities;
using Unity.Mathematics;

[GenerateAuthoringComponent]
public struct Heading : IComponentData
{
  public float3 Value;
  public int HeadingIndex;
}

        The preceding code creates a new component called Heading. It contains a value of type float3 and an optional HeadingIndex integer property.

        The component is also marked as generated and you can manually create data for the component using the Editor context menu or implementing the IComponentData interface.

        After you create and inject a component, it can be added to multiple entities:

void CreateEntities()
{
  Entity entity1 = EntityManager.CreateEntity(typeof(Heading));
  EntityManager.SetComponentData(entity1, new Heading { Value = new float3(0, 0, 1), HeadingIndex = 1 });

  Entity entity2 = EntityManager.CreateEntity(typeof(Heading));
  EntityManager.SetComponentData(entity2, new Heading { Value = new float3(1, 0, 0), HeadingIndex = 2 });

  Entity entity3 = EntityManager.CreateEntity(typeof(Heading));
  EntityManager.SetComponentData(entity3, new Heading { Value = new float3(0, 1, 0), HeadingIndex = 3 });
}

### 7. System - ComponentSystem

        The system is a group of component processing codes, and the system will be called and executed once in the main loop cycle.

        The following code shows how to create a custom system in Unity ECS:

using Unity.Entities;

public class MyFirstSystem : ComponentSystem
{
    struct Components
    {
        public readonly int Length;
        public ComponentDataArray<Heading> Heading;
    }

    [Inject] private Components _components;

    protected override void OnUpdate()
    {
        for (int i = 0; i < _components.Length; i++)
        {
            Heading heading = _components.Heading[i];
            Debug.Log($"Entity {i} has Heading {heading.Value}");
        }
    }
}

        The preceding code defines a new system named MyFirstSystem. In this system, the .OnUpdate() method will be called once every frame, and it will iterate over all entities that have a Heading component, and print the value of the component.

Note the following characteristics:

• The system uses the generics feature of C#.

• It uses the Inject tag.

• It uses ComponentDataArray in the component reference.

• The .OnUpdate() method is not called in the constructor.

### 8. Batch processing and concurrency

        In ECS, the system is optimized through batch processing to improve its performance.

        The following code demonstrates how to access multiple component instances of the same type through Batch:

using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;

[UpdateAfter(typeof(TransformSystemGroup))]
public class MovementSystem : JobComponentSystem
{
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        float deltaTime = Time.DeltaTime;
        float3 upVector = new float3(0, 1, 0);

        var jobHandle = Entities.ForEach((ref Translation translation, ref Heading heading, ref Speed speed) =>
        {
            float3 axis = math.cross(upVector, heading.Value);
            quaternion rotation = quaternion.AxisAngle(axis, speed.Value * deltaTime);
            translation.Value += math.mul(rotation, heading.Value) * speed.Value * deltaTime;
        }).Schedule(inputDeps);

        return jobHandle;
    }
}

        As mentioned above, the .OnUpdate() method will be called every frame, and it will iterate over all entities that have Heading, Translation, and Speed ​​components.

        The ForEach() method in the system uses the job (Job) concurrency scheduling system in order to obtain better performance when accessing and modifying entities.

### 9. Conclusion

        In Unity ECS, Entity Component System (ECS) is a more decoupled programming paradigm.

        Unity ECS is especially suitable for data-heavy games, applications and systems because it really takes advantage of modern hardware concurrency.

        By experimenting and trying, you can implement ECS in your own Unity game or application.

Guess you like

Origin blog.csdn.net/Asklyw/article/details/129944367