这个示例的内容是鼠标控制cube旋转,点击到哪里,范围内的cube变色和旋转
1.第一个关键点:鼠标发射线
//函数中掉用了托管代码,不能使用[BurstCompile]标签
public void OnUpdate(ref SystemState state)
{
//获取射线的数据组件
var hit = SystemAPI.GetSingletonRW<Hit>();
//射线数据初始化
hit.ValueRW.HitChanged = false;
//调用托管api
if (Camera.main == null || !Input.GetMouseButton(0))
{
return;
}
//设置射线数据组件
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (new Plane(Vector3.up, 0f).Raycast(ray, out var dist))
{
hit.ValueRW.HitChanged = true;
hit.ValueRW.Value = ray.GetPoint(dist);
}
}
//射线数据组件
//public struct Hit : IComponentData
//{
// 射线的位置
// public float3 Value;
// 射线的状态
// public bool HitChanged;
//}
需要注意的是:对于这种类全局单例功能,场景中只需要添加一次hit 数据组件
2.世界空间鼠标范围,设置cube的状态
SetStateSystem功能:设置颜色,旋转,添加旋转,移除旋转,激活旋转,不激活旋转,这些功能通过Mode枚举进行切换,调度不同的实体作业
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
//获取配置数据 预制体 Size鼠标范围,Radius鼠标半径,Mode模式
var config = SystemAPI.GetSingleton<Config>();
//获取射线数据
var hit = SystemAPI.GetSingleton<Hit>();
//射线的状态
if (!hit.HitChanged)
{
#if UNITY_EDITOR
SystemAPI.GetSingletonRW<StateChangeProfilerModule.FrameData>().ValueRW.SetStatePerf = 0;
#endif
return;
}
//鼠标范围
var radiusSq = config.Radius * config.Radius;
//ECB:开始时的命令缓冲,
var ecbSystem = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
//需要调度作业完成,才能执行下面逻辑
state.Dependency.Complete();
//非托管性能工具时间戳
var before = ProfilerUnsafeUtility.Timestamp;
//配置中的模式字段,不能模式执行不同的作业
if (config.Mode == Mode.VALUE)
{
new SetValueJob
{
RadiusSq = radiusSq,
Hit = hit.Value
}.ScheduleParallel();
}
else if (config.Mode == Mode.STRUCTURAL_CHANGE)
{
new AddSpinJob
{
RadiusSq = radiusSq,
Hit = hit.Value,
ECB = ecbSystem.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter()
}.ScheduleParallel();
new RemoveSpinJob
{
RadiusSq = radiusSq,
Hit = hit.Value,
ECB = ecbSystem.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter()
}.ScheduleParallel();
}
else if (config.Mode == Mode.ENABLEABLE_COMPONENT)
{
new EnableSpinJob
{
RadiusSq = radiusSq,
Hit = hit.Value,
ECB = ecbSystem.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter()
}.ScheduleParallel();
new DisableSpinJob
{
RadiusSq = radiusSq,
Hit = hit.Value,
}.ScheduleParallel();
}
//需要调度作业完成,才能执行下面逻辑
state.Dependency.Complete();
var after = ProfilerUnsafeUtility.Timestamp;
#if UNITY_EDITOR
// profiling 性能计算和设置
var conversionRatio = ProfilerUnsafeUtility.TimestampToNanosecondsConversionRatio;
var elapsed = (after - before) * conversionRatio.Numerator / conversionRatio.Denominator;
SystemAPI.GetSingletonRW<StateChangeProfilerModule.FrameData>().ValueRW.SetStatePerf = elapsed;
#endif
}
}
/// <summary>
/// 实体作业,设置cube状态,执行主要的逻辑
/// </summary>
[BurstCompile]
partial struct SetValueJob : IJobEntity
{
public float RadiusSq;
public float3 Hit;
//所有拥有 URPMaterialPropertyBaseColor,Spin,LocalTransform三个数据组件的实体都会执行函数中的逻辑
// URPMaterialPropertyBaseColor 内置的URP材质组件
//Spin 旋转标识
void Execute(ref URPMaterialPropertyBaseColor color, ref Spin spin, in LocalTransform transform)
{
if (math.distancesq(transform.Position, Hit) <= RadiusSq)
{
color.Value = (Vector4)Color.red;
spin.IsSpinning = true;
}
else
{
color.Value = (Vector4)Color.white;
spin.IsSpinning = false;
}
}
}
/// <summary>
/// 对于所有没有spin组件的实体,执行这个job
/// </summary>
[WithNone(typeof(Spin))]
[BurstCompile]
partial struct AddSpinJob : IJobEntity
{
public float RadiusSq;
public float3 Hit;
public EntityCommandBuffer.ParallelWriter ECB;
void Execute(Entity entity, ref URPMaterialPropertyBaseColor color, in LocalTransform transform,
[ChunkIndexInQuery] int chunkIndex)
{
// If cube is inside the hit radius.
if (math.distancesq(transform.Position, Hit) <= RadiusSq)
{
color.Value = (Vector4)Color.red;
ECB.AddComponent<Spin>(chunkIndex, entity);
}
}
}
/// <summary>
/// 对于所有有spin组件的实体,执行这个job
/// </summary>
[WithAll(typeof(Spin))]
[BurstCompile]
partial struct RemoveSpinJob : IJobEntity
{
public float RadiusSq;
public float3 Hit;
public EntityCommandBuffer.ParallelWriter ECB;
void Execute(Entity entity, ref URPMaterialPropertyBaseColor color, in LocalTransform transform,
[ChunkIndexInQuery] int chunkIndex)
{
// If cube is NOT inside the hit radius.
if (math.distancesq(transform.Position, Hit) > RadiusSq)
{
color.Value = (Vector4)Color.white;
ECB.RemoveComponent<Spin>(chunkIndex, entity);
}
}
}
[WithNone(typeof(Spin))]
[BurstCompile]
public partial struct EnableSpinJob : IJobEntity
{
public float RadiusSq;
public float3 Hit;
public EntityCommandBuffer.ParallelWriter ECB;
void Execute(Entity entity, ref URPMaterialPropertyBaseColor color, in LocalTransform transform,
[ChunkIndexInQuery] int chunkIndex)
{
// If cube is inside the hit radius.
if (math.distancesq(transform.Position, Hit) <= RadiusSq)
{
color.Value = (Vector4)Color.red;
ECB.SetComponentEnabled<Spin>(chunkIndex, entity, true);
}
}
}
[BurstCompile]
public partial struct DisableSpinJob : IJobEntity
{
public float RadiusSq;
public float3 Hit;
void Execute(Entity entity, ref URPMaterialPropertyBaseColor color, in LocalTransform transform,
EnabledRefRW<Spin> spinnerEnabled)
{
// If cube is NOT inside the hit radius.
if (math.distancesq(transform.Position, Hit) > RadiusSq)
{
color.Value = (Vector4)Color.white;
spinnerEnabled.ValueRW = false;
}
}
}
主要的意思是:因为对于ecs,通过组件标识查找物体的,所以如果需要我们整体操作实体的时候,需要像代码中的操作一样,例如:我要设置鼠标范围的方块,通过spin组件标识在鼠标范围内的方块,鼠标外的方块没有spin组件,然后在作业逻辑中,需要执行两段代码,一个是查找有spin标识的实体,一个是查找没有spin标识的实体,这个是一个示例说明:对于不同的实体,拥有不同的标识,然后执行不同的job,上面代码完合成一个job,对于所有的实体,整体进行判断