New Input Multiple controllers not working...

Hi all,

Pulling my hair out over this, I’ve read everything, or what feels like everything in the documentation on this… I’m developing a top down 2D game with the emphasis being on 4 players, although it can and will be played with less. I have 4 player objects / prefabs already in my scene, so I won’t be instantiating anything. I have 2 Xbox 360 controllers plugged in which Unity is receiving data from as can be seen in Input Debugger.

I have a player input component on 2 of the game objects, both using the same input actions map. But both controllers control all 4 objects in-game. Neither controller is being auto assigned to a player input, leaving 2 objects, uncontrollable. All I keep reading is PlayerInput will do this, and PlayerInput will assign that… what am I missing???

I’ve read about performpairwithdevice etc but unsure how to go about using it… and am unsure where ever that will solve my issues. Input Debugger is showing 2 users (User#0, User#1), and both controllers.

So if I understand the docs correctly, I need to somehow assign these controllers (devices), to each user, then each user to each PlayerInput (but doesn’t playerInput, do this automatically). I’ve read about PlayerInput.user, but am not having any luck with that either to check if each player input has an assigned user.

Below are screenshots of my inspector, Input Action and snippets of code.

Thank you in advance for any help, advice and replies.

5358282--541578--upload_2020-1-10_20-41-43.png

   void Start()
    {
        //set variable rb to this ship's rigidbody component
        rb = GetComponent<Rigidbody2D>();
    }

    void Awake()
    {

        controls = new PlayerControls();

        //sets the fire method to the Fire input action in actionmap
        controls.PlayerShip.Fire.started += ctx => Fire();

        controls.PlayerShip.Reset.started += ctx => Reset();

        //sets move to the X and Y values coming from the input action
        controls.PlayerShip.Move.performed += ctx => move = ctx.ReadValue<Vector2>();
        controls.PlayerShip.Move.canceled += ctx => move = Vector2.zero;

I’ve realised after reading on another post, that the code i’m using to move my players doesn’t allow for local multiplayer, and I will have to use methods created by player input (OnMove(), OnFire() etc).

So I have set all that up, but now player Input is now not assigning a device to the user… input debugger shows a device active and with receiving input… I’ve removed the other player objects from my scene so this is currently the only object with a Player Input Component.

I did quickly add another player object too see if the second Player Input component would create a second User and it did, but didn’t assign my currently wired in Xbox360 controller to either… I thought the PlayerInput component was supposed to control all the auto assign side of things…

Below is a screenshot of the game running in-editor. I’ve highlighted the area where device is showing as empty. I can’t find reference to this anywhere, so unsure where ever a device would actually show up here or not.

Any help, or advice in the right direction is greatly appreciated! @Kurt-Dekker you helped me with my last issue, do you have any ideas with this?? Thank you!

2 Likes

Code it like this as opposed to using the lambda functions.

   Vector3 move;
    Vector2 moveDir;
    float aim;
    Vector2 aimDir;
    void Start()
    {
        player = Instantiate(playerObj, new Vector3(0.0f, 1.0f, 0.0f), Quaternion.identity);
        player.GetComponent<MCPlayerinit>().playNum = playNum;
    }
    void Update()
    {
        Move();
        Aim();
    }
    void Move()
    {
        move = new Vector3(moveDir.x, 0.0f, moveDir.y) * moveSpeed * Time.deltaTime;
        player.transform.Translate(move, Space.World);
    }
    void Aim()
    {
        Vector3 lookTowards = new Vector3(aimDir.x,0.0f,aimDir.y);
        if (lookTowards != Vector3.zero)
        {
            player.transform.rotation = Quaternion.LookRotation(lookTowards);
        }
    }
    void OnMove(InputValue value)
    {
        moveDir = value.Get<Vector2>();
    }
    void OnAim(InputValue value)
    {
        aimDir = value.Get<Vector2>();
    }

This coupled with a player input manager object (A gameobject with the Player Input Manger Component)
should do the trick. A lot of tutorials are misleading when it comes to this.

& Here, watch this tutorial.
The brackeys one as well as some others don’t really use the feature properly for local multiplayer.

2 Likes

What if we do not simply have prefabs for our players?

Then make your players into prefabs? If you’re not using prefabs now is a good time to start :wink:

You either start using prefabs or you span your players and register the devices by hand.

If you’re using the Input System 1.0, there’s a bit more documentation about how to manage multiple controllers and there’s one super useful doc right HERE.

You can set up a single variable for each controller (such as var gamepad_One; var gamepad_Two; etc.) and then setting up which is the latest gamepad to have been used. This method requires you to have all the controls set up in a single script which mean you got to have a “central” input manager of some sort.

You can set up an array of gamepads ( var allGamepads = Gamepad.all; ) and then compare to find which, in the array, is being used by each player in row. It’s kinda like the previous option, but easier to use as you can just, then, use integer and array to point out the source (like allGamepads[1] from the code example above if the player One uses the second registered gamepad.) This, again, requires you to create a sort of “central” input manager that manage all inputs done by the players in a single script.

If you prefer an approach that has a bit more “adaptability”, you could make it so that each player’s prefab or character has their own Player Input component pre-attributed and directly target those Player Input instances from each individual player prefab instantiated. (Each players’ instance has its own controls script and its own Player Input component. When instantiating the player’s instance, you set up the variable in its control script so that it knows which gamepad should control its Player Input.

This last approach is great, but involve a bit more work in managing the input as you need to make sure things are done in the proper order. For example, you could pre-register the actual Gamepad (like in the first example above) on a general script and then, when needed, attribute each gamepad to each player’s instance as they are instantiated. It’s a bit of a work-around, but it ensure that this remains under controls.

The one down side of all of those approaches comes from the potential of a really simple, but bad situation.
What happens if a player… unplug a controller mid-game?

From that, it’s possible check when a device is unplugged and when a device is plugged back in.
If the same device is plugged back in, there’s nothing you need to do as the registered Gamepad variable is actually link to the gamepad unique number and not just as a generic gamepad. (You might get some error though depending on how you read the unplugged gamepad while it’s unplugged.)
In the Devices doc of the Input System, here’s the text that explains it:
The Input System keeps track of disconnected Devices in InputSystem.disconnectedDevices. If one of these Devices reconnects later, the Input System can detect that the Device was connected before, and reuses its InputDevice instance. This allows the PlayerInputManager to reassign the Device to the same user again.

The thing is more complex when it’s a case where a player actually change his controller. This is a possible action if the player was using a certain controller that ran out of battery and don’t have any spare batteries nor a way to charge/plug it. Maybe the player will, then, switch to a wired controller which, ultimately, is different.

For this, you got to do some quick check with something like shown in the link above:

InputSystem.onDeviceChange +=
        (device, change) =>
        {
            switch (change)
            {
                case InputDeviceChange.Added:
                    // New Device.
                    break;
                case InputDeviceChange.Disconnected:
                    // If this is happening, activate some boolean that will tell the game that a controller is now "missing".
                    break;
                case InputDeviceChange.Connected:
                    // Plugged back in.
                    break;
                case InputDeviceChange.Removed:
                    // Remove from Input System entirely; by default, Devices stay in the system once discovered.
                    break;
                default:
                    // Always includes a default case for when a unused case is being called. Leave it empty.
                    break;
            }
        }

As you can see, you can get whenever a device got plugged back in, when a new device was plugged in and when one is disconnected. Now, how you set up to relink with a new device depends on how you set them up in the first place. You got to have some kind of menu/pause/warning that activates when the case InputDeviceChange.Disconnected; is being activate/called from which you can get the player with the missing controller to press a button on the new controller to set it up and replace the disconnected controller.

2 Likes

I am implementing a on screen joystick using the new input system and i have bound it to the gamepad left stick control but, for some reason, the input is very bad. If I move the joystick around, it will give the correct Vector2 value sometimes and then suddenly go to 0,0 even when it is being moved. And it keeps flickering like this.
I even tried to use the Input Debugger to see if there is any problem with the gamepad input itself but the left stick value in the debugger is fine. It is only in the game that it is behaving like this

i’ve been trying to use player input manager, but one controller controls all players for some reason

1 Like

I am having the same problem and looking for a practical solution, “but i still haven1t found…”. Below links of same doubt.

https://www.reddit.com/r/Unity3D/comments/eqq07o/multiple_controllers_through_input_action_system/

In case anyone still has this problem, here is how worked around it:

• Gave my players some negative Controlled ID
• When a button from any controller is pressed all players will receive this input, because yes, I could not figure out a solution for this.
• So before the code triggered by the controller is executed I check if my player controller ID matches the device ID passed within the context passed by the controller when a button is pressed.

public void ButtonXPressed(InputAction.CallbackContext context)
        {
            if (controllerID < 0)
                AssignControllerID(context.control.device.deviceId);

            if (context.control.device.deviceId != controllerID)
                return;

            WhateverXIsSupposedToDoFunction();
        }

Now, since all players will receive this command, all players will have the same ID. So before assigning the ID you should check if the ID being assigned already matches the ID of another player.

In my case, my game has always only 2 players. If the number of players for your game changes, or can increase too much, you might have to get a bit creative with how you perform these checks.

void AssignController(int deviceID)
        {
            if (P1)
            {
                if (controllerID < 0) 
                    controllerID = deviceID;
                return;
            }
//If P2
            int p1id = GameObject.Find("GameController1").GetComponent<GameController>().controllerID;
            if (p1id < 0 || p1id == deviceID)
                return;

            if (controllerID < 0)
                controllerID = deviceID;
        }

Finally. It is important to reset the ID back to a negative value when the controller disconnects.

It worked fine for me, hopefully it helps anyone else who comes across this.