VR4Medical/ICI/Library/PackageCache/com.unity.xr.interaction.toolkit@42ef3600567b/Tests/Runtime/LocomotionInputTests.cs
2025-07-29 13:45:50 +03:00

202 lines
8.9 KiB
C#

using System.Collections;
using NUnit.Framework;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Processors;
using UnityEngine.TestTools;
using UnityEngine.TestTools.Utils;
using UnityEngine.XR.Interaction.Toolkit.Locomotion;
using UnityEngine.XR.Interaction.Toolkit.Locomotion.Movement;
using UnityEngine.XR.Interaction.Toolkit.Locomotion.Turning;
namespace UnityEngine.XR.Interaction.Toolkit.Tests
{
[TestFixture]
class LocomotionInputTests : InputTestFixture
{
enum ForwardSource
{
Default,
Camera,
Controller,
}
[TearDown]
public override void TearDown()
{
TestUtilities.DestroyAllSceneObjects();
base.TearDown();
}
[UnityTest]
public IEnumerator MoveInDefaultDirection()
{
return MoveInDirection(ForwardSource.Default);
}
[UnityTest]
public IEnumerator MoveInCameraDirection()
{
return MoveInDirection(ForwardSource.Camera);
}
[UnityTest]
public IEnumerator MoveInControllerDirection()
{
return MoveInDirection(ForwardSource.Controller);
}
IEnumerator MoveInDirection(ForwardSource forwardSource)
{
// Create a stick control to serve as the input action source for the move provider
var gamepad = InputSystem.InputSystem.AddDevice<Gamepad>();
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var actionMap = asset.AddActionMap("Locomotion");
var action = actionMap.AddAction("Move",
InputActionType.Value,
"<Gamepad>/leftStick");
var inputActionReference = ScriptableObject.CreateInstance<InputActionReference>();
inputActionReference.Set(action);
action.Enable();
var xrOrigin = TestUtilities.CreateXROrigin();
var rigTransform = xrOrigin.Origin.transform;
var cameraTransform = xrOrigin.Camera.transform;
// Rotate the camera to face a different direction than rig forward to test
// that the move provider will move with respect to a selected forward object.
cameraTransform.Rotate(0f, 45f, 0f);
var cameraForward = cameraTransform.forward;
Assert.That(rigTransform.forward, Is.Not.EqualTo(cameraForward).Using(Vector3ComparerWithEqualsOperator.Instance));
// Create a controller object to serve as another forward source
var controllerGO = new GameObject("Controller");
controllerGO.transform.SetParent(xrOrigin.CameraFloorOffsetObject.transform, false);
controllerGO.transform.Rotate(0f, -45f, 0f);
var controllerForward = controllerGO.transform.forward;
Assert.That(rigTransform.forward, Is.Not.EqualTo(controllerForward).Using(Vector3ComparerWithEqualsOperator.Instance));
// Config continuous move on XR Origin
var mediator = xrOrigin.gameObject.AddComponent<LocomotionMediator>();
mediator.GetComponent<XRBodyTransformer>().xrOrigin = xrOrigin;
var moveProvider = xrOrigin.gameObject.AddComponent<ContinuousMoveProvider>();
moveProvider.mediator = mediator;
moveProvider.leftHandMoveInput.inputActionReference = inputActionReference;
moveProvider.moveSpeed = 1f;
switch (forwardSource)
{
case ForwardSource.Default:
break;
case ForwardSource.Camera:
moveProvider.forwardSource = xrOrigin.Camera.transform;
break;
case ForwardSource.Controller:
moveProvider.forwardSource = controllerGO.transform;
break;
default:
Assert.Fail($"Unhandled {nameof(ForwardSource)}={forwardSource}");
break;
}
// See Script Execution Order diagram https://docs.unity3d.com/Manual/ExecutionOrder.html
// This test will begin after Update() during the yield null/yield WaitForSeconds/yield StartCoroutine stage.
// The move provider will process input during Update() of the next frame, and scale the move based on Time.deltaTime.
// After yielding for 1 second with the stick pushed forward, the stick will be released back to center.
// The move provider will process the release during Update() of the next frame, and should not apply any more movement.
// Partially push stick directly forward.
// This tests that the move speed will be scaled by the input magnitude.
var input = new Vector2(0f, 0.5f);
var processedInput = new StickDeadzoneProcessor().Process(input);
Set(gamepad.leftStick, input);
var startTime = Time.time;
for (var i = 0; i < 60; ++i) // wait for 60 frames.
yield return Application.isBatchMode ? null : new WaitForEndOfFrame();
var actualPosition = rigTransform.position;
var actualDistance = Vector3.Distance(Vector3.zero, actualPosition);
var expectedDistance = processedInput.magnitude * moveProvider.moveSpeed * (Time.time - startTime);
Assert.That(actualDistance, Is.EqualTo(expectedDistance).Within(1e-5f));
switch (forwardSource)
{
case ForwardSource.Default:
case ForwardSource.Camera:
Assert.That(actualPosition, Is.EqualTo(cameraForward * expectedDistance).Using(Vector3ComparerWithEqualsOperator.Instance));
break;
case ForwardSource.Controller:
Assert.That(actualPosition, Is.EqualTo(controllerForward * expectedDistance).Using(Vector3ComparerWithEqualsOperator.Instance));
break;
default:
Assert.Fail($"Unhandled {nameof(ForwardSource)}={forwardSource}");
break;
}
// Stop moving
Set(gamepad.leftStick, Vector2.zero);
yield return Application.isBatchMode ? null : new WaitForEndOfFrame();
// ReSharper disable Unity.InefficientPropertyAccess -- Property value accessed after yield
Assert.That(Vector3.Distance(actualPosition, rigTransform.position), Is.EqualTo(0f));
// ReSharper restore Unity.InefficientPropertyAccess
}
[UnityTest]
public IEnumerator SmoothTurn()
{
// Create a stick control to serve as the input action source for the turn provider
var gamepad = InputSystem.InputSystem.AddDevice<Gamepad>();
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var actionMap = asset.AddActionMap("Locomotion");
var action = actionMap.AddAction("Turn",
InputActionType.Value,
"<Gamepad>/rightStick");
var inputActionReference = ScriptableObject.CreateInstance<InputActionReference>();
inputActionReference.Set(action);
action.Enable();
var xrOrigin = TestUtilities.CreateXROrigin();
var rigTransform = xrOrigin.Origin.transform;
// Config continuous turn on XR Origin
var mediator = xrOrigin.gameObject.AddComponent<LocomotionMediator>();
mediator.GetComponent<XRBodyTransformer>().xrOrigin = xrOrigin;
var turnProvider = xrOrigin.gameObject.AddComponent<ContinuousTurnProvider>();
turnProvider.mediator = mediator;
turnProvider.leftHandTurnInput.inputActionReference = inputActionReference;
turnProvider.turnSpeed = 60f;
// Partially push stick directly right.
// This tests that the turn speed will be scaled by the input magnitude.
var input = new Vector2(0.5f, 0f);
var processedInput = new StickDeadzoneProcessor().Process(input);
Set(gamepad.rightStick, input);
var startTime = Time.time;
for (var i = 0; i < 60; ++i) // wait for 60 frames.
yield return Application.isBatchMode ? null : new WaitForEndOfFrame();
var turnAmount = processedInput.magnitude * turnProvider.turnSpeed * (Time.time - startTime);
var actualRotation = rigTransform.rotation;
Assert.That(actualRotation, Is.EqualTo(Quaternion.Euler(0f, turnAmount, 0f)).Using(QuaternionEqualityComparer.Instance));
// Stop turning
Set(gamepad.rightStick, Vector2.zero);
yield return Application.isBatchMode ? null : new WaitForEndOfFrame();
// ReSharper disable Unity.InefficientPropertyAccess -- Property value accessed after yield
Assert.That(actualRotation, Is.EqualTo(rigTransform.rotation).Using(QuaternionEqualityComparer.Instance));
// ReSharper restore Unity.InefficientPropertyAccess
}
}
}