官方原文是说 I am not saying pooling as a general strategy when using game objects or other things that allocate GC memory is not useful. I am saying that pooling instantiated entities in Unity.Entities has no real merit because we solved the problem at the root by having pooling of chunks internally + very fast instantiation when done in batch.
There are no GC allocations if you take the recommended fast paths. Specifically if you only have shared components + struct IComponentData. Then instantiating entities will allocate zero GC memory.
As an example. A few years back in the Nordeus large battle field demo. We were spawning 10.000 arrows per frame. That was not doing any pooling at all...
class IComponentData does exist, if you actually use them, then yes there is GC memory allocations and that means it would be useful to do pooling. But the point here is, if you care about performance & GC memory pressure, then don't use class components in your game at all for this and many other reasons. They can be convenient in some cases but they are not performance by default....
Hence the recommendation when working with entities is that you use struct components + don't do pooling as a default approach. And measure specific cases where thats not enough.
但是某些特殊情况(比如大量实例粒子之类的)有对象池优化起来还是方便点,所以整了个,大多数情况还是不建议使用对象池
using Unity.Burst; using Unity.Collections; using Unity.Entities; using UnityEngine; namespace DOTS.Extensions { [BurstCompile] public partial struct AddBulletEntity : IJobEntity { public EntityCommandBuffer.ParallelWriter ECB; public BufferLookup<PoolBuffer> PoolBufferLookAt; public ComponentLookup<PoolEntity> PoolEntityLookAt; public void Execute(Entity entity, in ToAfterTransform after) { var hasInit = PoolEntityLookAt.TryGetComponent(entity, out var tag); if (!hasInit) { Debug.LogError("是不是忘记初始化了1"); } if (!PoolBufferLookAt.TryGetBuffer(tag.Tag, out var buffer)) { Debug.LogError("是不是忘记初始化了2"); } buffer.Add(new PoolBuffer(entity)); ECB.RemoveComponent<ToAfterTransform>(0, entity); ECB.SetEnabled(0, entity, false); } } public partial struct RemoveToPool : ISystem { private BufferLookup<PoolBuffer> _poolBufferLookAt; private ComponentLookup<PoolEntity> _poolEntityLookAt; [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate<ToAfterTransform>(); _poolEntityLookAt = state.GetComponentLookup<PoolEntity>(); _poolBufferLookAt = state.GetBufferLookup<PoolBuffer>(); } [BurstCompile] public void OnUpdate(ref SystemState state) { _poolBufferLookAt.Update(ref state); _poolEntityLookAt.Update(ref state); using var ecb = new EntityCommandBuffer(Allocator.TempJob); var job = new AddBulletEntity { PoolBufferLookAt = _poolBufferLookAt, PoolEntityLookAt = _poolEntityLookAt, ECB = ecb.AsParallelWriter(), }; state.Dependency = job.Schedule(state.Dependency); state.Dependency.Complete(); ecb.Playback(state.EntityManager); } } }
using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; using UnityEngine; namespace DOTS.Extensions { public struct ToAfterTransform : IComponentData { } public struct PoolEntityTag : IComponentData { } public struct PoolEntity : IComponentData { public Entity Tag; } public struct PoolBuffer : IBufferElementData { public Entity Val; public PoolBuffer(Entity val) { Val = val; } } public static class EntityPool { public static void InitPool(this Entity entity, EntityCommandBuffer.ParallelWriter ecb) { var e = ecb.CreateEntity(0); ecb.AddComponent<PoolEntityTag>(0, e); ecb.AddBuffer<PoolBuffer>(0, e); ecb.AddComponent(0, entity, new PoolEntity { Tag = e }); } /// <summary> /// 创建对象 /// </summary> /// <param name="entity">对象</param> /// <param name="ecb">ECB</param> /// <param name="poolLookAt"></param> /// <param name="bufferLookAt">对象池</param> /// <param name="updateTransform">是否自定义LocalTransform</param> /// <param name="transform">自定义的LocalTransform</param> /// <returns></returns> public static Entity Spawn(this Entity entity, EntityCommandBuffer.ParallelWriter ecb, ComponentLookup<PoolEntity> poolLookAt, BufferLookup<PoolBuffer> bufferLookAt, bool updateTransform = false, LocalTransform transform = default) { Entity e; var hasInit = poolLookAt.TryGetComponent(entity, out var tag); if (!hasInit) { Debug.LogError("是不是忘记初始化了"); } if (hasInit && bufferLookAt.TryGetBuffer(tag.Tag, out var buffer) && buffer.Length > 0) { var lastIndex = buffer.Length - 1; e = buffer[lastIndex].Val; buffer.RemoveAt(lastIndex); ecb.SetEnabled(0, e, true); } else { e = ecb.Instantiate(0, entity); } if (!updateTransform) { transform = new LocalTransform { Position = float3.zero, Scale = 1, Rotation = quaternion.identity }; } ecb.SetComponent(0, e, transform); return e; } private static readonly float3 Float3Out = new(100000, 100000, 100000); /// <summary> /// 回池 /// </summary> /// <param name="entity">回池对象</param> /// <param name="ecb">ECB</param> /// <param name="poolLookAt">池子对象</param> /// <param name="bufferLookAt">对象池</param> public static void Despawn(this Entity entity, EntityCommandBuffer.ParallelWriter ecb) { ecb.SetComponent(0, entity, new LocalTransform { Position = Float3Out, Scale = 100 }); ecb.AddComponent<ToAfterTransform>(0, entity); } } }
用起来也很简单 先将需要对象池控制的Entity入池
Entity.InitPool(ecbCreate.AsParallelWriter());
然后使用Entity.Spawn和Entity.Despawn创建销毁(创建用原始对象,销毁用需要被销毁的对象)