Files
Project-M/Assets/_Project/Scripts/Simulation/Combat/ProjectileMoveSystem.cs
T
2026-06-03 22:41:27 -07:00

44 lines
1.9 KiB
C#

using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Transforms;
namespace ProjectM.Simulation
{
/// <summary>
/// Predicted projectile integrator: advances every live <see cref="Projectile"/> along its
/// replicated planar (XZ) <see cref="Projectile.Direction"/> at its baked <see cref="Projectile.Speed"/>,
/// accumulating <see cref="Projectile.DistanceTravelled"/>. Runs inside the prediction loop on the
/// owning client (re-simulated on rollback) and once per tick on the server, after
/// <see cref="AbilityFireSystem"/> has spawned this tick's shots; filtered to <see cref="Simulate"/>
/// so only predicted ghosts move. Deterministic by construction: uses <c>SystemAPI.Time.DeltaTime</c>
/// (the fixed tick step) only — no wall-clock, no <c>System.Random</c>. Pure motion: range expiry and
/// destruction are server-authoritative in <c>ProjectileDamageSystem</c>, so this system never
/// performs structural changes and is fully idempotent across rollback re-simulation.
/// </summary>
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
[UpdateAfter(typeof(AbilityFireSystem))]
[BurstCompile]
public partial struct ProjectileMoveSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
float dt = SystemAPI.Time.DeltaTime;
foreach (var (transform, projectile) in
SystemAPI.Query<RefRW<LocalTransform>, RefRW<Projectile>>()
.WithAll<Simulate>())
{
float step = projectile.ValueRO.Speed * dt;
projectile.ValueRW.LastStep = step;
float3 dir = new float3(projectile.ValueRO.Direction.x, 0f, projectile.ValueRO.Direction.y);
transform.ValueRW.Position += dir * step;
projectile.ValueRW.DistanceTravelled += step;
}
}
}
}