UnityDots学习(四)
官方案例HelloCube和Tank学习研究:
HelloCube:
通用部分:
使用Authoring根据Inspector的勾选添加为Entity添加不同Component。然后每个System会根据实体添加的Component运行不同的System逻辑。
1. MainThread
简单构造System
先看System接口定义:
[RequireImplementors]public interface ISystem{/// <summary>/// Called when this system is created./// </summary>/// <remarks>/// Implement an `OnCreate` function to set up system resources when it is created.////// `OnCreate` is invoked before the the first time <see cref="ISystemStartStop.OnStartRunning(ref SystemState)"/>/// and <see cref="OnUpdate"/> are invoked./// </remarks>/// <param name="state">The <see cref="SystemState"/> backing this system instance</param>[RequiredMember]void OnCreate(ref SystemState state) { }/// <summary>/// Called when this system is destroyed./// </summary>/// <remarks>/// Systems are destroyed when the application shuts down, the World is destroyed, or you/// call <see cref="World.DestroySystem"/>. In the Unity Editor, system destruction occurs when you exit/// Play Mode and when scripts are reloaded./// </remarks>/// <param name="state">The <see cref="SystemState"/> backing this system instance</param>[RequiredMember]void OnDestroy(ref SystemState state) { }/// <summary>/// Implement `OnUpdate` to perform the major work of this system./// </summary>/// <remarks>/// <p>/// By default, the system invokes `OnUpdate` once every frame on the main thread./// To skip OnUpdate if all of the system's [EntityQueries] are empty, use the/// [RequireMatchingQueriesForUpdateAttribute]. To limit when OnUpdate is invoked, you can/// specify components that must exist, or queries that match specific Entities. To do/// this, call <see cref="SystemState.RequireForUpdate{T}"/> or/// <see cref="SystemState.RequireForUpdate(EntityQuery)"/>/// in the system's OnCreate method. For more information, see <see cref="SystemState.ShouldRunSystem"/>./// </p>/// <p>/// You can instantiate and schedule an <see cref="IJobChunk"/> instance; you can use the/// [C# Job System] or you can perform work on the main thread. If you call <see cref="EntityManager"/> methods/// that perform structural changes on the main thread, be sure to arrange the system order to minimize the/// performance impact of the resulting [sync points]./// </p>////// [sync points]: xref:concepts-structural-changes/// [C# Job System]: https://docs.unity3d.com/Manual/JobSystem.html/// [EntityQueries]: xref:Unity.Entities.EntityQuery/// [RequireMatchingQueriesForUpdateAttribute]: xref:Unity.Entities.RequireMatchingQueriesForUpdateAttribute/// </remarks>/// <param name="state">The <see cref="SystemState"/> backing this system instance</param>[RequiredMember]void OnUpdate(ref SystemState state) { }}
有3处接口
OnCreate里可以添加是否让System在OnUpdate里运行
类似:
当至少有一个Entity包含ExecuteIJobEntity组件。会激活Update
OnDestory就是在System被销毁时运行
OnUpdate里实现了简单的旋转。
这里Query是查询当前符合要求Component的实体。
RW是即可读又可写,RO是只可读,在system里不可改变其属性
2. IJobEntity
该案例是把System里执行的逻辑换成Job,这样程序的运行可以利用多核在不用非在主线程里运行。这样会提升CPU的使用率
这个Job实现的Schedule可以实现在代码里顺序执行。
比如增加下列代码测试,就可以发现物体上下移动了
3. Aspects
上述的读取数据还得根据情况写RW,RO。
Unity提供了一个接口如图:
不需要去传入值,且函数名可以自定义
在使用时直接查询就可以正确去找到对应拥有Component组件的实体进行遍历。这个比较方便
需要注意是必须用
readonly partial进行修饰。
可以看到Unity自己扩展定义后的内容
4. Prefabs
public partial struct SpawnSystem : ISystem{uint updateCounter;[BurstCompile]public void OnCreate(ref SystemState state){// This call makes the system not update unless at least one entity in the world exists that has the Spawner component.state.RequireForUpdate<Spawner>();state.RequireForUpdate<ExecutePrefabs>();}[BurstCompile]public void OnUpdate(ref SystemState state){// Create a query that matches all entities having a RotationSpeed component.// (The query is cached in source generation, so this does not incur a cost of recreating it every update.)var spinningCubesQuery = SystemAPI.QueryBuilder().WithAll<RotationSpeed>().Build();// Only spawn cubes when no cubes currently exist.if (spinningCubesQuery.IsEmpty){var prefab = SystemAPI.GetSingleton<Spawner>().Prefab;// Instantiating an entity creates copy entities with the same component types and values.var instances = state.EntityManager.Instantiate(prefab, 500, Allocator.Temp);// Unlike new Random(), CreateFromIndex() hashes the random seed// so that similar seeds don't produce similar results.var random = Random.CreateFromIndex(updateCounter++);foreach (var entity in instances){// Update the entity's LocalTransform component with the new position.var transform = SystemAPI.GetComponentRW<LocalTransform>(entity);transform.ValueRW.Position = (random.NextFloat3() - new float3(0.5f, 0, 0.5f)) * 20;}}}}
拿到对象实例,这里是Dots需要用结构体,所以跟Unity之前的GameObject生成有所区别 var prefab = SystemAPI.GetSingleton<Spawner>().Prefab;
这里是生成实例的DOTS版本
var instances = state.EntityManager.Instantiate(prefab, 500, Allocator.Temp);
public partial struct FallAndDestroySystem : ISystem{[BurstCompile]public void OnCreate(ref SystemState state){state.RequireForUpdate<ExecutePrefabs>();}[BurstCompile]public void OnUpdate(ref SystemState state){// rotationfloat deltaTime = SystemAPI.Time.DeltaTime;foreach (var (transform, speed) inSystemAPI.Query<RefRW<LocalTransform>, RefRO<RotationSpeed>>()){// ValueRW and ValueRO both return a ref to the actual component value.// The difference is that ValueRW does a safety check for read-write access while// ValueRO does a safety check for read-only access.transform.ValueRW = transform.ValueRO.RotateY(speed.ValueRO.RadiansPerSecond * deltaTime);}// An EntityCommandBuffer created from EntityCommandBufferSystem.Singleton will be// played back and disposed by the EntityCommandBufferSystem when it next updates.var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);// Downward vectorvar movement = new float3(0, -SystemAPI.Time.DeltaTime * 5f, 0);// WithAll() includes RotationSpeed in the query, but// the RotationSpeed component values will not be accessed.// WithEntityAccess() includes the Entity ID as the last element of the tuple.foreach (var (transform, entity) inSystemAPI.Query<RefRW<LocalTransform>>().WithAll<RotationSpeed>().WithEntityAccess()){transform.ValueRW.Position += movement;if (transform.ValueRO.Position.y < 0){// Making a structural change would invalidate the query we are iterating through,// so instead we record a command to destroy the entity later.ecb.DestroyEntity(entity);}}}}
Dots版本的删除实体:
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
ecb.DestroyEntity(entity);
BeginSimulationEntityCommandBufferSystem.Singleton:我的理解就是全部关闭实例Manager
EntityCommandBuffer:是每个实体管理的控制器,类似GameObjectManager。所有的创建销毁实体都是通过他来控制。