using Unity.Mathematics; namespace ProjectM.Simulation { /// /// Pure mapping from authoritative planar velocity + facing to client animation blend params. /// Forward (along facing) -> MoveZ+, right of facing -> MoveX+ (matches the slim controller's 2D /// strafe tree: +X=right, +Z=forward). EditMode-tested, no World needed. Burst-safe (no enums/strings). /// public static class AnimParamMath { /// World velocity (KinematicCharacterBody.RelativeVelocity for the owner, /// or LocalTransform position-delta/dt for remotes). Y is ignored. /// Normalized world planar facing (PlayerFacing.Direction, world XZ as x,y). May be zero. /// EffectiveCharacterStats.MoveSpeed (normalizer; guarded > 0). /// x = MoveX (strafe, -1..1), y = MoveZ (fwd/back, -1..1), z = Speed (0..1). public static float3 LocomotionParams(float3 planarVelocity, float2 facing, float maxSpeed) { float2 vWorld = planarVelocity.xz; float safeMax = math.max(maxSpeed, 1e-4f); float speed = math.saturate(math.length(vWorld) / safeMax); // Degenerate facing -> default to world +Z so the basis is well-defined. float2 fwd = math.lengthsq(facing) > 1e-6f ? math.normalize(facing) : new float2(0f, 1f); float2 right = new float2(fwd.y, -fwd.x); // 90deg clockwise in XZ (right-hand of forward) float2 local = new float2(math.dot(vWorld, right), math.dot(vWorld, fwd)) / safeMax; local = math.clamp(local, -1f, 1f); return new float3(local.x, local.y, speed); } } }