之前看的差不多了,就从之前的进度开始写
运行效果
代码解读
SliderHandler.cs
using Unity.Entities;
using UnityEngine;
using UnityEngine.UI;
namespace HelloCube.FixedTimestep
{
public class SliderHandler : MonoBehaviour
{
public Text sliderValueText;
public void OnSliderChange()
{
float fixedFps = GetComponent<Slider>().value;
// WARNING: accessing World.DefaultGameObjectInjectionWorld is a broken pattern in non-trivial projects.
// GameObject interaction with ECS should generally go in the other direction: rather than having
// GameObjects access ECS data and code, ECS systems should access GameObjects.
var fixedSimulationGroup = World.DefaultGameObjectInjectionWorld
?.GetExistingSystemManaged<FixedStepSimulationSystemGroup>();
if (fixedSimulationGroup != null)
{
// The group timestep can be set at runtime:
fixedSimulationGroup.Timestep = 1.0f / fixedFps;
// The current timestep can also be retrieved:
sliderValueText.text = $"{(int)(1.0f / fixedSimulationGroup.Timestep)} updates/sec";
}
}
}
}
在scene中将唯一的拖动条将OnValueChange设为OnSliderChange,将slider对应的文本赋值到sliderValueText。
当Slider的值改变时,获取ECS中的FixedStepSimulationSystemGroup(还能这样的?!)。这个Group内的代码都相当于传统Mono中的FixedUpdate。最后在if语句中改变Fps并更改slider的文本内容。
FixedStepSimulationSystemGroup与SimulationSystemGroup的结构如图:
ProjectileAuthoring
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace HelloCube.FixedTimestep
{
public class ProjectileAuthoring : MonoBehaviour
{
class Baker : Baker<ProjectileAuthoring>
{
public override void Bake(ProjectileAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent<Projectile>(entity);
}
}
}
public struct Projectile : IComponentData
{
public float SpawnTime;
public float3 SpawnPos;
}
}
该代码附在预制体内,即上方示例图中生成的片状方块内。
设计了个名为Projectile的data,记录生成的时间与生成的位置。
Baker将预制体从GameObject转换成ECS内的Entity,并加上Projectile的data
DefaultRateSpawnerAuthoring
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace HelloCube.FixedTimestep
{
public class DefaultRateSpawnerAuthoring : MonoBehaviour
{
public GameObject projectilePrefab;
class Baker : Baker<DefaultRateSpawnerAuthoring>
{
public override void Bake(DefaultRateSpawnerAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.None);
var spawnerData = new DefaultRateSpawner
{
Prefab = GetEntity(authoring.projectilePrefab, TransformUsageFlags.Dynamic),
SpawnPos = GetComponent<Transform>().position,
};
AddComponent(entity, spawnerData);
}
}
}
public struct DefaultRateSpawner : IComponentData
{
public Entity Prefab;
public float3 SpawnPos;
}
}
该代码附在Subscene的VaribleSpawner内
MoveProjectilesSystem
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
namespace HelloCube.FixedTimestep
{
public partial struct MoveProjectilesSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<ExecuteFixedTimestep>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
new MoveJob
{
TimeSinceLoad = (float)SystemAPI.Time.ElapsedTime,
ProjectileSpeed = 5.0f,
ECBWriter = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter()
}.ScheduleParallel();
}
}
[BurstCompile]
public partial struct MoveJob : IJobEntity
{
public float TimeSinceLoad;
public float ProjectileSpeed;
public EntityCommandBuffer.ParallelWriter ECBWriter;
void Execute(Entity projectileEntity, [ChunkIndexInQuery] int chunkIndex, ref LocalTransform transform,
in Projectile projectile)
{
float aliveTime = TimeSinceLoad - projectile.SpawnTime;
if (aliveTime > 5.0f)
{
ECBWriter.DestroyEntity(chunkIndex, projectileEntity);
}
transform.Position.x = projectile.SpawnPos.x + aliveTime * ProjectileSpeed;
}
}
}
OnCreate内为所有示例的通用部分,在所有示例都在一个工程文件下的情况,控制是否启用system。
OnUpdate内获取FixedStepSimulationSystemGroup内最晚执行的commandBuffer,和当前时间还有移动速度载入MoveJob内。
MoveJob过滤所有有Entity,LocalTransform和Projectile的Entity,并执行Execute内的操作。
场景内所有的移动都通过该system完成
FixedRateSpawnerAuthoring
基本与前文DefaultRateSpawnerAuthoring相同,附在Subscene的FixedSpawner内
DefaultRateSpawnerSystem
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
namespace HelloCube.FixedTimestep
{
public partial struct DefaultRateSpawnerSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<ExecuteFixedTimestep>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
float spawnTime = (float)SystemAPI.Time.ElapsedTime;
foreach (var spawner in
SystemAPI.Query<RefRW<DefaultRateSpawner>>())
{
var projectileEntity = state.EntityManager.Instantiate(spawner.ValueRO.Prefab);
var spawnPos = spawner.ValueRO.SpawnPos;
spawnPos.y += 0.3f * math.sin(5.0f * spawnTime);
SystemAPI.SetComponent(projectileEntity, LocalTransform.FromPosition(spawnPos));
SystemAPI.SetComponent(projectileEntity, new Projectile
{
SpawnTime = spawnTime,
SpawnPos = spawnPos,
});
}
}
}
}
控制生成位置,通过Projectile记录生成时间和生成位置
FixedRateSpawnerSystem
基本与DefaultRateSpawnerSystem相同。唯一不同处为
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
public partial struct FixedRateSpawnerSystem : ISystem
让该system在FixedStepSimulationSystemGroup内。