using NUnit.Framework; using ProjectM.Simulation; using Unity.Mathematics; namespace ProjectM.Tests { public class AnimParamMathTests { const float Eps = 1e-3f; static readonly float2 Fwd = new float2(0f, 1f); // facing world +Z const float Max = 5f; [Test] public void ZeroVelocity_AllZero() { var r = AnimParamMath.LocomotionParams(float3.zero, Fwd, Max); Assert.AreEqual(0f, r.x, Eps); Assert.AreEqual(0f, r.y, Eps); Assert.AreEqual(0f, r.z, Eps); } [Test] public void FullForward_MoveZ1_Speed1() { var r = AnimParamMath.LocomotionParams(new float3(0f, 0f, Max), Fwd, Max); Assert.AreEqual(0f, r.x, Eps); Assert.AreEqual(1f, r.y, Eps); Assert.AreEqual(1f, r.z, Eps); } [Test] public void FullBackward_MoveZNeg1() { var r = AnimParamMath.LocomotionParams(new float3(0f, 0f, -Max), Fwd, Max); Assert.AreEqual(0f, r.x, Eps); Assert.AreEqual(-1f, r.y, Eps); Assert.AreEqual(1f, r.z, Eps); } [Test] public void StrafeRight_MoveX1() { // facing +Z -> right = (+X). world velocity +X -> MoveX = +1. var r = AnimParamMath.LocomotionParams(new float3(Max, 0f, 0f), Fwd, Max); Assert.AreEqual(1f, r.x, Eps); Assert.AreEqual(0f, r.y, Eps); Assert.AreEqual(1f, r.z, Eps); } [Test] public void StrafeLeft_MoveXNeg1() { var r = AnimParamMath.LocomotionParams(new float3(-Max, 0f, 0f), Fwd, Max); Assert.AreEqual(-1f, r.x, Eps); } [Test] public void HalfForward_HalfSpeed() { var r = AnimParamMath.LocomotionParams(new float3(0f, 0f, Max * 0.5f), Fwd, Max); Assert.AreEqual(0.5f, r.y, Eps); Assert.AreEqual(0.5f, r.z, Eps); } [Test] public void OverMax_SpeedClampsTo1() { var r = AnimParamMath.LocomotionParams(new float3(0f, 0f, Max * 3f), Fwd, Max); Assert.AreEqual(1f, r.z, Eps); Assert.AreEqual(1f, r.y, Eps); } [Test] public void RotatedFacing_ProjectsIntoFrame() { // facing world +X; velocity world +X should read as forward (MoveZ+), not strafe. var r = AnimParamMath.LocomotionParams(new float3(Max, 0f, 0f), new float2(1f, 0f), Max); Assert.AreEqual(0f, r.x, Eps); Assert.AreEqual(1f, r.y, Eps); } [Test] public void DegenerateFacing_DefaultsToWorldForward() { var r = AnimParamMath.LocomotionParams(new float3(0f, 0f, Max), float2.zero, Max); Assert.AreEqual(1f, r.y, Eps); // treated as facing +Z } [Test] public void ZeroMaxSpeed_NoNaN_NoDivByZero() { // guard: safeMax = max(maxSpeed, 1e-4) -> finite output even with maxSpeed 0. var r = AnimParamMath.LocomotionParams(new float3(0f, 0f, 1f), Fwd, 0f); Assert.IsFalse(float.IsNaN(r.x) || float.IsNaN(r.y) || float.IsNaN(r.z)); Assert.AreEqual(1f, r.z, Eps); // clamps to 1 } } }