SwitchToActionMap() causing Errors and Editor Freeze.

I’m currently having an issue where using SwitchToActionMap() is causing a series of errors.

I’m using a default InputActions asset which I have added an “Interact” button to, and mapped it to the E button and X Button on a gamepad. It’s set to trigger only on “Press Only”.

The errors occur when I’m interacting with a car object. The button press triggers this function.

    public void Interact(InputAction.CallbackContext context)
    {
        if(InteractObject != null)
        {
            InteractObject.GetComponent<Interactable>().Interact();
        }
    }

Which, just in case the interaction triggers twice (which seems to be the case), activates this:

    override public void Interact ()
    {
        if (PlayerInside == false)
        {
            EnterCar();
        }
    }

    public void EnterCar()
    {
        var playerInput = Player.GetComponent<PlayerInput>();
        playerInput.SwitchCurrentActionMap("Tank");
        PlayerInside = true;
        print("Enter Car");
    }

This causes 3 errors:

  1. Interaction index out of range, originating in the ResetInteractionState function of InputActionState.cs
  2. IndexOutOfRangeException while executing ‘started’ callbacks of ‘Player/Interact[/Keyboard/e]’
  3. IndexOutOfRangeException: Index was outside the bounds of the array

It still works and switches Action Map, but when I try to switch back to “Player” by using an ExitCar() script, it causes the editor to freeze.

Is this a bug? Am I doing something wrong?

Which version of the package does this happen with? If this is happening with 1.0.0-preview.6, please file a ticket with the Unity bug reporter and we’ll have a look. InputActionState definitely should not be throwing out-of-bounds exceptions.

I submitted a bug report. It happens on a fresh project with 2019.3.2 & 2019.3.7 with 1.0.0-preview.6. Tested on both my laptop and my desktop.

Thanks for responding!

1 Like

Had a similar problem, solved it by making sure that it only triggered once when switching action map.

For anyone who comes across this in the future, the issue is that context has phases in it, such as started, performed, and cancelled (see this document). For something like a press and hold or a joystick, these can be more useful and immediately apparent, but for a button the started and performed events will be the same thing, which will invoke that callback twice. This is NOT immediately apparent. The solution however is to add an if statement in the invoked callback:

public void Interact(InputAction.CallbackContext context)
    {
        if (context.performed)
        {
            if(InteractObject != null)
            {
                InteractObject.GetComponent<Interactable>().Interact();
            }
        }
    }

Now the callback will still be invoked a few times but your code block will only run once.

I hope this helps someone!

I had the same problem. I wanted to remove one player in a local-multiplayer type setup that was previously joined via the PlayerInputManager.
Since there is no RemovePlayer(int index) function or something similar coming with the PlayerInputManager, I tried to just delete the gameobject which has the PlayerInput component on it.

This however threw errors:

Apparently, this happened because I was trying to remove a player through a function that gets called via an input event of that same player (player presses a button to leave the lobby). I have encountered this a few times now, that performing changes to PlayerInput within the same frame of a callback context happening messes things up (for instance switching InputActionMaps within input events causes the same input to fire again).

My solution was to wait two frames via a Coroutine (one didnt work) and then remove the player. This seemed to work fine! However, if the player happends to press something within that next frame, the errors might still occur - even though that’s unlikely, its not impossible so maybe try to catch that or wrap all input handlers within some bool check that then is prevented from executing.

So Instead of something like this:

        public void RemovePlayer(Player player)
        {
            Destroy(player.gameObject);
        }

Do this:

        public void RemovePlayer(Player player)
        {
            StartCoroutine(C_DelayedRemovePlayer(player));
        }

        private IEnumerator C_DelayedRemovePlayer(Player player)
        {
            yield return new WaitForEndOfFrame();
            yield return new WaitForEndOfFrame();
            Destroy(player.gameObject);
            yield return null;
        }