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 xrRig        = TestUtilities.CreateXRRig();
            var rigTransform = xrRig.rig.transform;

            // Config continuous turn on XR rig
            var locoSys = xrRig.gameObject.AddComponent <LocomotionSystem>();

            locoSys.xrRig = xrRig;
            var turnProvider = xrRig.gameObject.AddComponent <ActionBasedContinuousTurnProvider>();

            turnProvider.system             = locoSys;
            turnProvider.leftHandTurnAction = new InputActionProperty(action);
            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;

            yield return(new WaitForSeconds(1f));

            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(new WaitForSeconds(0.1f));

            // ReSharper disable Unity.InefficientPropertyAccess -- Property value accessed after yield
            Assert.That(actualRotation, Is.EqualTo(rigTransform.rotation).Using(QuaternionEqualityComparer.Instance));
            // ReSharper restore Unity.InefficientPropertyAccess
        }
    public void Controls_CanHaveStickDeadzones()
    {
        const string json = @"
            {
                ""name"" : ""MyDevice"",
                ""extend"" : ""Gamepad"",
                ""controls"" : [
                    {
                        ""name"" : ""leftStick"",
                        ""processors"" : ""stickDeadzone(min=0.1,max=0.9)""
                    }
                ]
            }
        ";

        InputSystem.RegisterLayout(json);
        var device = (Gamepad)InputSystem.AddDevice("MyDevice");

        var firstState = new GamepadState {
            leftStick = new Vector2(0.05f, 0.05f)
        };
        var secondState = new GamepadState {
            leftStick = new Vector2(0.5f, 0.5f)
        };

        InputSystem.QueueStateEvent(device, firstState);
        InputSystem.Update();

        Assert.That(device.leftStick.ReadValue(), Is.EqualTo(default(Vector2)));

        InputSystem.QueueStateEvent(device, secondState);
        InputSystem.Update();

        var processedVector = new StickDeadzoneProcessor {
            min = 0.1f, max = 0.9f
        }.Process(new Vector2(0.5f, 0.5f));

        Assert.That(device.leftStick.ReadValue(), Is.EqualTo(processedVector));

        // Deadzoning on the stick axes is independent so we shouldn't see equivalent values on
        // the axes here.
        Assert.That(device.leftStick.x.ReadValue(), Is.Not.EqualTo(processedVector.x));
        Assert.That(device.leftStick.y.ReadValue(), Is.Not.EqualTo(processedVector.y));
    }
        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 xrRig           = TestUtilities.CreateXRRig();
            var rigTransform    = xrRig.rig.transform;
            var cameraTransform = xrRig.cameraGameObject.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(xrRig.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 rig
            var locoSys = xrRig.gameObject.AddComponent <LocomotionSystem>();

            locoSys.xrRig = xrRig;
            var moveProvider = xrRig.gameObject.AddComponent <ActionBasedContinuousMoveProvider>();

            moveProvider.system             = locoSys;
            moveProvider.leftHandMoveAction = new InputActionProperty(action);
            moveProvider.moveSpeed          = 1f;

            switch (forwardSource)
            {
            case ForwardSource.Default:
                break;

            case ForwardSource.Camera:
                moveProvider.forwardSource = xrRig.cameraGameObject.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;

            yield return(new WaitForSeconds(1f));

            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(new WaitForSeconds(0.1f));

            // ReSharper disable Unity.InefficientPropertyAccess -- Property value accessed after yield
            Assert.That(Vector3.Distance(actualPosition, rigTransform.position), Is.EqualTo(0f));
            // ReSharper restore Unity.InefficientPropertyAccess
        }