using Unity.Burst; using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; using Unity.NetCode; using Unity.Collections; using Unity.Rendering; namespace Asteroids.Client { [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)] public partial struct AsteroidSwitchPredictionSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); state.RequireForUpdate(); state.RequireForUpdate(); state.RequireForUpdate(); } [BurstCompile] public void OnUpdate(ref SystemState state) { var settings = SystemAPI.GetSingleton(); if (settings.predictionRadius <= 0) return; var stateEntityManager = state.EntityManager; if (!SystemAPI.TryGetSingletonEntity(out var playerEnt) || !stateEntityManager.HasComponent(playerEnt)) return; var playerPos = stateEntityManager.GetComponentData(playerEnt).Position; var parallelEcb = SystemAPI.GetSingleton().CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter(); var ghostPredictionSwitchingQueues = SystemAPI.GetSingletonRW().ValueRW; new SwitchToPredictedGhostViaRange { playerPos = playerPos, parallelEcb = parallelEcb, predictedQueue = ghostPredictionSwitchingQueues.ConvertToPredictedQueue, enterRadiusSq = settings.predictionRadius*settings.predictionRadius, }.ScheduleParallel(); var radiusPlusMargin = settings.predictionRadius + settings.predictionRadiusMargin; new SwitchToInterpolatedGhostViaRange { playerPos = playerPos, parallelEcb = parallelEcb, interpolatedQueue = ghostPredictionSwitchingQueues.ConvertToInterpolatedQueue, exitRadiusSq = radiusPlusMargin * radiusPlusMargin, }.ScheduleParallel(); } [BurstCompile] [WithNone(typeof(PredictedGhost), typeof(SwitchPredictionSmoothing))] [WithAll(typeof(AsteroidTagComponentData))] partial struct SwitchToPredictedGhostViaRange : IJobEntity { public float3 playerPos; public float enterRadiusSq; public NativeQueue.ParallelWriter predictedQueue; public EntityCommandBuffer.ParallelWriter parallelEcb; void Execute(Entity ent, [EntityIndexInQuery] int entityIndexInQuery, in LocalTransform transform) { if (math.distancesq(playerPos, transform.Position) < enterRadiusSq) { predictedQueue.Enqueue(new ConvertPredictionEntry { TargetEntity = ent, TransitionDurationSeconds = 1.0f, }); parallelEcb.AddComponent(entityIndexInQuery, ent, new URPMaterialPropertyBaseColor { Value = new float4(0, 1, 0, 1) }); } } } [BurstCompile] [WithNone(typeof(SwitchPredictionSmoothing))] [WithAll(typeof(PredictedGhost), typeof(AsteroidTagComponentData))] partial struct SwitchToInterpolatedGhostViaRange : IJobEntity { public float3 playerPos; public float exitRadiusSq; public NativeQueue.ParallelWriter interpolatedQueue; public EntityCommandBuffer.ParallelWriter parallelEcb; void Execute(Entity ent, [EntityIndexInQuery] int entityIndexInQuery, in LocalTransform transform) { if (math.distancesq(playerPos, transform.Position) > exitRadiusSq) { interpolatedQueue.Enqueue(new ConvertPredictionEntry { TargetEntity = ent, TransitionDurationSeconds = 1.0f, }); parallelEcb.RemoveComponent(entityIndexInQuery, ent); } } } } }