using Unity.Burst; using Unity.Entities; using Unity.Mathematics; using Unity.NetCode; using Unity.Transforms; namespace ProjectM.Simulation { /// /// Predicted aim/facing: writes from twin-stick Aim, falling back to /// the movement direction when Aim is zero (controller-first directional aim). Also turns the /// ghost transform toward the facing direction for top-down presentation. When there is no input /// this tick the previous facing is held. Deterministic (pure math); filtered to /// so it runs only for predicted ghosts. /// [UpdateInGroup(typeof(PredictedSimulationSystemGroup))] [BurstCompile] public partial struct PlayerAimSystem : ISystem { [BurstCompile] public void OnUpdate(ref SystemState state) { float dt = SystemAPI.Time.DeltaTime; foreach (var (facing, transform, input, stats) in SystemAPI.Query, RefRW, RefRO, RefRO>() .WithAll().WithDisabled()) { float2 aim = input.ValueRO.Aim; if (math.lengthsq(aim) < 1e-6f) aim = input.ValueRO.Move; // fall back to movement heading if (math.lengthsq(aim) < 1e-6f) continue; // no input this tick: keep last facing aim = math.normalize(aim); // Rate-limited turn: rotate the current facing toward the aim target by at most // TurnRateRadiansPerSec * dt this tick. Deterministic (pure planar math, fixed-step dt) // so it replays correctly on rollback; the first tick (uninitialized facing) snaps. float2 cur = facing.ValueRO.Direction; float2 dir; if (math.lengthsq(cur) < 1e-6f) { dir = aim; // uninitialized facing -> snap to target } else { cur = math.normalize(cur); float maxStep = stats.ValueRO.TurnRateRadiansPerSec * dt; float angle = math.acos(math.clamp(math.dot(cur, aim), -1f, 1f)); if (angle <= maxStep) { dir = aim; // within reach this tick } else { float sign = (cur.x * aim.y - cur.y * aim.x) >= 0f ? 1f : -1f; math.sincos(maxStep * sign, out float sn, out float cs); dir = math.normalize(new float2(cur.x * cs - cur.y * sn, cur.x * sn + cur.y * cs)); } } facing.ValueRW.Direction = dir; float3 forward = new float3(dir.x, 0f, dir.y); transform.ValueRW.Rotation = quaternion.LookRotationSafe(forward, math.up()); } } } }