public void Actions_CanPerformDoubleTapInteraction()
    {
        var gamepad = InputSystem.AddDevice <Gamepad>();

        runtime.advanceTimeEachDynamicUpdate = 0;

        var action = new InputAction(binding: "<Gamepad>/buttonSouth", interactions: "multitap(tapTime=0.5,tapDelay=0.75,tapCount=2)");

        action.Enable();
        using (var trace = new InputActionTrace())
        {
            trace.SubscribeTo(action);

            // Press button.
            runtime.currentTime = 1;
            InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 1);
            InputSystem.Update();

            var actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(1));
            Assert.That(actions[0].phase, Is.EqualTo(InputActionPhase.Started));
            Assert.That(actions[0].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[0].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[0].time, Is.EqualTo(1).Within(0.00001));

            trace.Clear();

            // Release before tap time and make sure the double tap cancels.
            runtime.currentTime = 12;
            InputSystem.QueueStateEvent(gamepad, new GamepadState(), 1.75);
            InputSystem.Update();

            actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(1));
            Assert.That(actions[0].phase, Is.EqualTo(InputActionPhase.Canceled));
            Assert.That(actions[0].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[0].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[0].time, Is.EqualTo(1.75).Within(0.00001));

            trace.Clear();

            // Press again and then release before tap time. Should see only the start from
            // the initial press.
            runtime.currentTime = 2.5;
            InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 2);
            InputSystem.QueueStateEvent(gamepad, new GamepadState(), 2.25);
            InputSystem.Update();

            actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(1));
            Assert.That(actions[0].phase, Is.EqualTo(InputActionPhase.Started));
            Assert.That(actions[0].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[0].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[0].time, Is.EqualTo(2).Within(0.00001));
            Assert.That(actions[0].ReadValue <float>(), Is.EqualTo(1).Within(0.00001));

            trace.Clear();

            // Wait for longer than tapDelay and make sure we're seeing a cancellation.
            runtime.currentTime = 4;
            InputSystem.Update();

            actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(1));
            Assert.That(actions[0].phase, Is.EqualTo(InputActionPhase.Canceled));
            Assert.That(actions[0].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[0].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[0].time, Is.EqualTo(4).Within(0.00001));
            Assert.That(actions[0].ReadValue <float>(), Is.EqualTo(0).Within(0.00001));// Button isn't pressed currently.

            trace.Clear();

            // Now press and release within tap time. Then press again within delay time but release
            // only after tap time. Should we started and canceled.
            runtime.currentTime = 6;
            InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 4.7);
            InputSystem.QueueStateEvent(gamepad, new GamepadState(), 4.9);
            InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 5);
            InputSystem.QueueStateEvent(gamepad, new GamepadState(), 5.9);
            InputSystem.Update();

            actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(2));
            Assert.That(actions[0].phase, Is.EqualTo(InputActionPhase.Started));
            Assert.That(actions[0].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[0].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[0].time, Is.EqualTo(4.7).Within(0.00001));
            Assert.That(actions[0].ReadValue <float>(), Is.EqualTo(1).Within(0.00001));
            Assert.That(actions[1].phase, Is.EqualTo(InputActionPhase.Canceled));
            Assert.That(actions[1].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[1].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[1].time, Is.EqualTo(5.9).Within(0.00001));
            Assert.That(actions[1].ReadValue <float>(), Is.EqualTo(0).Within(0.00001));

            trace.Clear();

            // Finally perform a full, proper double tap cycle.
            runtime.currentTime = 8;
            InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 7);
            InputSystem.QueueStateEvent(gamepad, new GamepadState(), 7.25);
            InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 7.5);
            InputSystem.QueueStateEvent(gamepad, new GamepadState(), 7.75);
            InputSystem.Update();

            actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(2));
            Assert.That(actions[0].phase, Is.EqualTo(InputActionPhase.Started));
            Assert.That(actions[0].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[0].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[0].time, Is.EqualTo(7).Within(0.00001));
            Assert.That(actions[0].ReadValue <float>(), Is.EqualTo(1).Within(0.00001));
            Assert.That(actions[1].phase, Is.EqualTo(InputActionPhase.Performed));
            Assert.That(actions[1].interaction, Is.TypeOf <MultiTapInteraction>());
            Assert.That(actions[1].control, Is.SameAs(gamepad.buttonSouth));
            Assert.That(actions[1].time, Is.EqualTo(7.75).Within(0.00001));
            Assert.That(actions[1].ReadValue <float>(), Is.Zero.Within(0.00001));
        }
    }
Example #2
0
    public void Actions_CanPerformPressInteraction()
    {
        var gamepad = InputSystem.AddDevice <Gamepad>();

        // We add a second input device (and bind to it), to test that the binding
        // conflict resolution will not interfere with the interaction handling.
        InputSystem.AddDevice <Keyboard>();

        // Test all three press behaviors concurrently.
        var pressOnlyAction = new InputAction("PressOnly", binding: "<Gamepad>/buttonSouth", interactions: "press");

        pressOnlyAction.AddBinding("<Keyboard>/a");
        var releaseOnlyAction = new InputAction("ReleaseOnly", binding: "<Gamepad>/buttonSouth", interactions: "press(behavior=1)");

        releaseOnlyAction.AddBinding("<Keyboard>/s");
        var pressAndReleaseAction = new InputAction("PressAndRelease", binding: "<Gamepad>/buttonSouth", interactions: "press(behavior=2)");

        pressAndReleaseAction.AddBinding("<Keyboard>/d");

        pressOnlyAction.Enable();
        releaseOnlyAction.Enable();
        pressAndReleaseAction.Enable();

        using (var trace = new InputActionTrace())
        {
            trace.SubscribeToAll();

            runtime.currentTime = 1;
            Press(gamepad.buttonSouth);

            var actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(5));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressOnlyAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Started).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressOnlyAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Performed).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressAndReleaseAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Started).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressAndReleaseAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Performed).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(releaseOnlyAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Started).And.With.Property("duration")
                        .EqualTo(0));

            trace.Clear();

            runtime.currentTime = 2;
            Release(gamepad.buttonSouth);

            actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(3));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(releaseOnlyAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Performed).And.With.Property("duration")
                        .EqualTo(1));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressAndReleaseAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Started).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressAndReleaseAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Performed).And.With.Property("duration")
                        .EqualTo(0));

            trace.Clear();

            runtime.currentTime = 5;
            Press(gamepad.buttonSouth);

            actions = trace.ToArray();
            Assert.That(actions, Has.Length.EqualTo(5));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressOnlyAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Started).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressOnlyAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Performed).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressAndReleaseAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Started).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(pressAndReleaseAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Performed).And.With.Property("duration")
                        .EqualTo(0));
            Assert.That(actions,
                        Has.Exactly(1).With.Property("action").SameAs(releaseOnlyAction).And.With.Property("phase")
                        .EqualTo(InputActionPhase.Started).And.With.Property("duration")
                        .EqualTo(0));
        }
    }