案例四:Hybrid(混合)ECS动态创建指定数量的小球Entity(实体)(Scne5)
Hybrid编程 GameObject转变为实体,使用unity的预制体来动态创建数量级雨滴下落。在上篇的基础上,将Unity预制体加入代理来实现。
脚本:DropData_Hybrid
using Unity.Entities;
[System.Serializable]
public struct DropData_Hybrid : IComponentData
{
public float delay;
public float velocity;
}
脚本:DropSystem_Hbrid
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
public class DropSystem_Hybrid : ComponentSystem
{
public float minHeight = -100; // 下落最低值
protected override void OnUpdate()
{
// 通过ECS提供的筛方式遍历场景中包含该数据组件的实体
Entities.ForEach((ref Translation translation, ref DropData dropData) =>
{
if (dropData.delay > 0)
{
dropData.delay -= Time.deltaTime;
}
else
{
if (translation.Value.y < minHeight)
{
translation.Value.y = 0;
dropData.velocity = Random.Range(1, 10);
dropData.delay = Random.Range(1, 10);
}
else
{
translation.Value.y -= dropData.velocity * Time.deltaTime;
}
}
});
}
}
代理脚本:DropDataProxy_Hybrid
using UnityEngine;
using Unity.Entities;
public class DropDataProxy_Hybrid : MonoBehaviour,IConvertGameObjectToEntity
{
public DropData dropData;
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
dstManager.AddComponentData<DropData>(entity, dropData);
}
}
脚本:Hybrid_DropMain
using UnityEngine;
using Unity.Entities;
using Unity.Collections;
using Unity.Rendering;
using Unity.Transforms;
public class Hybrid_DropMain : MonoBehaviour
{
public int entityCount = 1000;
public int entityRange = 100;
public int delaySpeed = 1;
public int dropSpeed = 1;
public Mesh mymesh;
public Material mymaterial;
public GameObject prefab;
void Start()
{
this.CreatePureMain();
}
void CreatePureMain()
{
#region 1.创建本地实体数组
// 世界自动创建的唯一实体管理器
EntityManager entityManager = World.Active.EntityManager;
NativeArray<Entity> entities = new NativeArray<Entity>(entityCount, Allocator.Temp);
/* 这里不需要手动创建实体原型了
// 创建一个原型模型(可以类比 GameObject.CreatePrimitive ), 函数参数代表该实体有哪些组件(注意要想实体在场景中显示必须有 Translation,RenderMesh,LoalToWorld这三个组件)
EntityArchetype entityArchetype = entityManager.CreateArchetype
(
typeof(Translation),
typeof(RenderMesh),
ComponentType.ReadWrite<LocalToWorld>(), // 实体的矩阵,
ComponentType.ReadOnly<DropData>() //
);
entityManager.CreateEntity(entityArchetype, entities);
*/
// unity 以提供了一个转换实体的公共类
Entity entity = GameObjectConversionUtility.ConvertGameObjectHierarchy(this.prefab, World.Active);
// 然后对这个实体进行实例化 想象成 Instantiate
entityManager.Instantiate(entity, entities);
#endregion
#region 2.初始化数组中的实体数据
for (int i = 0; i < entityCount; i++)
{
// 初始化位置信息
Translation translation = new Translation();
translation.Value = Random.insideUnitSphere * entityRange; // 设置位置为随机的球体
translation.Value.y = 0;
entityManager.SetComponentData<Translation>(entities[i], translation); // 设置位置信息
// 初始化延迟时间
entityManager.SetComponentData<DropData>(entities[i], new DropData { delay = Random.Range(1, 10), velocity = Random.Range(1, 100) });
// 因为使用的预制体 这里不再需要手动设置网格信息
// 给所有实体 设置网格 和材质 信息 这些UnityECS做了优化处理,使用共享网格材质的函数进行添加
//entityManager.SetSharedComponentData<RenderMesh>(entities[i], new RenderMesh { mesh = mymesh, material = mymaterial });
}
entities.Dispose(); // 这里把内存缓冲区释放掉
#endregion
}
}
如下图,将球体设置为预制体,将代理脚本挂在到其身上。注意添加ConvertToEntity脚本。
将预制体拖到Hybrid_DropMain的初始化prefab变量,如下图通过预制体方式生成了10万个小球,与上篇Stats数据相比:Batches增加了将近1万的batche,因为预制体上还有碰撞器,旋转信息等不必要的组件。这可以看出两者的性能区别之处!
远程项目仓库(github):https://github.com/h1031464180/UnityECS