Entities - Entity 的创建模式

发布于:2025-09-15 ⋅ 阅读:(20) ⋅ 点赞:(0)

1、 Unity 下创建游戏对象的过程

  • UnityEngine.GameObject是一个C#类,是Unity.Component实例的容器,并且必须有一个TransformComponent,可以组织成父子对象层级。
  • UnityEngine.Component也是一个C#类,里边有Update方法和其他方法在引擎的GameLoop时调用,并且可以被多个托管类型对象引用,包括GameObject和其他Component,Asset等
  • 托管对象无法使用Burst编译,所以除一些特殊类型外,一般来说无法在Jobs中使用,并且托管对象需要GC回收,所以创建和销毁GameObject和它们的Components开销较高。
  • 托管对象无法整体的在内存中打包,对Cache不友好

​​​​​​​

2、Unity 下创建 Entities 对象的模式

2.1 Authoring 模式创建

  • Entity SubScene时一个用于GameObject Baked的场景
  • Baking 将编辑器中的GameObject转换为写入Entity Scene的Entity也就是将Auhtoring下的数据转换为运行时数据。每次编辑EntityScene场景中的对象时都会触发Baking,并且是增量Baking.

  • Baker用来创建自定义数据,用来将自定义组件数据附加到所属的已经创建的Entity上。
  • Baking System用来处理Baker产生的数据输出,它只作用在Entity数据上,而不能作用在托管的Authoring数据上,可以使用Jobs和Burst。这个功能是可选的。

举个例子:

在 SubScene 下有一个 Cube,让他进行旋转,如果要让它旋转,先要将旋转的数据 Bake 在 Entity 对象上:

using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

namespace DOTS.DOD
{
    struct RotateSpeedComonent : IComponentData
    {
        public float rotateSpeed;
    }

    public class RotateSpeedAuthoring : MonoBehaviour
    {
        [Range(0, 360)] public float rotationSpeed = 360f;
        public class Baker : Baker<RotateSpeedAuthoring>
        {
            public override void Bake(RotateSpeedAuthoring authoring)
            {
                var entity = this.GetEntity(TransformUsageFlags.Dynamic);
                var data = new RotateSpeedComonent
                {
                    rotateSpeed = math.radians(authoring.rotationSpeed)
                };
                AddComponent<RotateSpeedComonent>(entity);
                SetComponent<RotateSpeedComonent>(entity, data);
            }
        }
    }
}

然后通过一个 System 来对这个带有这个 Component 的对象进行操作

using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;

namespace DOTS.DOD
{
    [BurstCompile]
    public partial struct CubeRotateSystem: ISystem
    {
        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            Debug.Log("[CubeRotateSystem] Start");
        }

        [BurstCompile]
        public void OnDestroy(ref SystemState state)
        {
            Debug.Log("[CubeRotateSystem] OnDestroy");
        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            float deltaTime = SystemAPI.Time.DeltaTime;
            foreach (var (transform, speed) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<RotateSpeedComonent>>())
            {
                transform.ValueRW = transform.ValueRO.RotateY(speed.ValueRO.rotateSpeed * deltaTime);
            }
        }
    }   
}

2.2 Runtime 模式创建

  • World是Enitites集合,每个Entity在一个世界中ID是唯一的,但也可能和其他世界的EntityID碰撞,所以通常情况下通过ID查找并不是一个安全的方式。
  • EnitityManager用来管理世界的所有Entities,有CreateEntity, DestroyEntity, AddComponent, RemoveComponent, GetComponentData,SetComponentData等接
  • Entity是一个整型 Id 与 Version 数据组成的结构体,会与Unity.Entities的Component有映射,Entity之间没有父子关系,id 和 Version 完全一样才是同一个 Entity。
  • Unity.Entities.IComponentData是一个C#结构体(或者类),可以被EntitiesID索引,但无法被托管对象引用(一般情况),并且没有任何方法(通常),在内存中紧密排列存储,通过Query访问更高效
  • System包括SystemBase与ISystem,SystemBase是托管类,简单,但运行在主线程,不能被burst编译,ISystem是Struct,非托管,可以被编译,相当快,但实现起来会有一点复杂。

// 创建新world
var world = new World( "test" ); 
// 获取World中的EntityManager
var entityManager=world.EntityManager;
// 创建Entity
Entity entity=entityManager.CreateEntity();
// 为创建的entity绑定Component
entityManager.AddComponent<CubeGeneratorByScript>(entity);
// 获取Component数据副本,并设置相关数据后再设置回对应的Component
var generator = entityManager.GetComponentData<CubeGeneratorByScript>(entity);
generator.cubecount=CubeCount;
entityManager.SetComponentData(entity,cubePrototype);

....

// System中实例化
var generator =SystemAPl.GetSingleton<CubeGeneratorByScript>();
var cubes = CollectionHelper.CreateNativeArray<Entity>(generator.cubeCount, Allocator.Temp);
state.entityManager.Instantiate(generator.cubeEntityProtoType, cubes);
cubes.Dispose();