This chapter is for you to analyze ECS cases, resources come from, you get it by yourselves:
https://github.com/Unity-Technologies/EntityComponentSystemSamples
ECS Case Analysis
1. ForEach - An Introduction Case of ECS
In this case, there are two components, one is RotationSpeedSystem_ForEach, and the other is ConvertToEntity.
1.1,ConvertToEntity-Entity
The function of this Component is to start the GameObject of the Inspector panel from the root node, convert the entire GameObject (including sub-objects) into Entity, and convert all existing convertible components into ECS Components.
In the ConvertToEntity code, the GameObjectConversionUtility.Convert() function is executed, and the 400 million "convertible" functions are based on the Convert() function only when the component implements the Convert() function of the interface IConvertGameObjectToEntity.
Finally, destroy the original GameObject
1.2,RotationSpeedAuthoring_ForEach-Convert
using System; using Unity.Entities; using Unity.Mathematics; using UnityEngine; [RequiresEntityConversion] public class RotationSpeedAuthoring_ForEach : MonoBehaviour, IConvertGameObjectToEntity { public float DegreesPerSecond; //So here's the Convert function in 1.1, in this Convert function //Pass in the parameters of MonoBehaviour as parameters of the RotationSpeed_ForEach component //And from angle to radian public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { var data = new RotationSpeed_ForEach { RadiansPerSecond = math.radians(DegreesPerSecond) }; dstManager.AddComponentData(entity, data); } }
1.3,RotationSpeed_ForEach-Component
Components in ECS inherit IComponentData and are struct types, whereas only data is stored in this structure.
using System; using Unity.Entities; // The Serializable attribute is intended to support the editor [Serializable] //Keep in mind that IComponentData is struct type public struct RotationSpeed_ForEach : IComponentData//First inherit IComponentData { public float RadiansPerSecond;//Data in components }
1.4,RotationSpeedSystem_ForEach-System
This part, which I have already described in my ECS (7), uses Component System to process data. Component System runs on the main thread, so it does not use multiple CPU kernels and is generally not recommended.
Component System provides the Entities.ForEach function, which calls ForEach in the OnUpdate() function of the system and passes in a lambda function, which takes the relevant components as parameters and performs the necessary work.
The third is to set up rotation animation for entities with Rotation Quaternion and Rotation Speed components
using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; using UnityEngine; //This system updates the entities of all RotationSpeed_ForEach and Rotation components in the scenario public class RotationSpeedSystem_ForEach : ComponentSystem { protected override void OnUpdate() { // Entities.ForEach runs in the main thread (Job is not used), but this is not recommended for better performance. // This is just a case for you to introduce ECS, and can quickly get started. Entities.ForEach((ref RotationSpeed_ForEach rotationSpeed, ref Rotation rotation) => { var deltaTime = Time.deltaTime; rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * deltaTime)); }); } }
1.5. Summary
So let's summarize the simplest steps to use ECS:
- Refer to 1.1 for the ECS entity you need to convert. Hang the ConvertToEntity script, it will automatically convert the convertible GameObject Component to Entity Component.
- If you want your own components, refer to 1.2 to implement Convert interface
2.1 Component Writing Reference 1.3 - Reference 1.4 traverses all entities through the simplest Component System, identifies the required entities, and then updates them.
- Add the update function you want in Foreach
So far, we have learned the most basic and simplest use of ECS.
2,ForEachWithEntityChanges-Prefabs To Entity
For the sake of looking convenient, we separate the code.
public class Spawner_ForEachWithEntityChanges : MonoBehaviour { public GameObject Prefab; public int CountX = 100; public int CountY = 100;
Here are the basic ways of writing.
void Start() { Entity prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(Prefab, World.Active); EntityManager entityManager = World.Active.EntityManager;
At this stage, we have done two things:
- Get the EntityManager in the current World (default World)
- Use the GameObjectConversionUtility.ConvertGameObjectHierarchy function to convert Prefab to Entity
for (int x = 0; x < CountX; x++) { for (int y = 0; y < CountX; y++) { Entity instance = entityManager.Instantiate(prefab); float3 position = transform.TransformPoint(new float3(x - CountX/2, noise.cnoise(new float2(x, y) * 0.21F) * 10, y - CountY/2)); entityManager.SetComponentData(instance, new Translation(){ Value = position }); entityManager.AddComponentData(instance, new MoveUp_ForEachWithEntityChanges()); entityManager.AddComponentData(instance, new MovingCube_ForEachWithEntityChanges()); } } } }
Finally, create more Entity using prefab that has been converted into Entity as a prototype
The position is randomly generated, and then we use SetComponentData to modify the position of the Entity in Translation (similar to the original Transform).
Then add MoveUp_ForEachWithEntityChanges and MovingCube_ForEachWithEntityChanges to Entity through AddComponentData
These two components are empty, and they are just Tag s to facilitate subsequent processing.
2.1. Component System Analysis
For the sake of looking convenient, we separate the code.
public class MovementSystem_ForEachWithEntityChanges : ComponentSystem { protected override void OnUpdate() {
Also used here is the Component System, which has been mentioned above.
Entities.WithAllReadOnly<MovingCube_ForEachWithEntityChanges, MoveUp_ForEachWithEntityChanges>().ForEach( (Entity id, ref Translation translation) => { var deltaTime = Time.deltaTime; translation = new Translation() { Value = new float3(translation.Value.x, translation.Value.y + deltaTime, translation.Value.z) }; if (translation.Value.y > 10.0f) EntityManager.RemoveComponent<MoveUp_ForEachWithEntityChanges>(id); } );
Unlike before, this time we used the WithAllReadOnly function to get entities with components -- obviously, there is no data in these two components, so read-only can save more performance than read/write (there are other advantages, please refer to the previous article).
So, now we have all the entities with these two components (MovingCube and MoveUp) and let them go up until the height is greater than 10, then remove the MoveUp_ForEachWithEntityChanges component.
Entities.WithAllReadOnly<MovingCube_ForEachWithEntityChanges>().WithNone<MoveUp_ForEachWithEntityChanges>().ForEach( (Entity id, ref Translation translation) => { translation = new Translation() { Value = new float3(translation.Value.x, -10.0f, translation.Value.z) }; EntityManager.AddComponentData(id, new MoveUp_ForEachWithEntityChanges()); } );
So in the following traversal, we get an entity with MovingCube_ForEachWithEntityChanges component, but no MoveUp_ForEachWithEntityChanges component -- because of the last cycle, the component with a height greater than 10 was beneficial -- so in this cycle, we need to weigh those components. New height settings, and then add MoveUp_ForEachWithEntityChanges components to them, so in the next cycle, they can be traversed by the above loop and continue to move up.
} }
The effect is as follows
2.2. Summary
In this case, we can convert Prefab into Entity and then use it as a prototype to generate more Entity. The steps are as follows:
- Create or obtain an EntityManager (for managing Entity)
- ConvertGameObject Hierarchy transforms Prefab into Entity through GameObjectConversionUtility.ConvertGameObjectHierarchy
- Set or add components for Entity (or add or set components by implementing the IConvertGameObjectToEntity interface, as in Case 1)