[Solved] Can the new Input system be used without the Player Input Component?

I am learning from some old tutorials that do not use the player input component, but as I am trying for now I have no luck using only the generated C# scripting without using the Player Input Component.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class Player_Input : MonoBehaviour
{

    public GameObject unitSelected;

    public Controls controls;

    private void Awake()
    {

        controls = new Controls();

    }

    private void OnEnable()
    {

        controls.Enable();
        controls.General.Attack.performed += ShootButton;
        controls.General.Move.performed += MoveButton;

    }

    private void OnDisable()
    {

        controls.General.Attack.performed -= ShootButton;
        controls.General.Move.performed -= MoveButton;
        controls.Disable();

    }

    public void ShootButton(InputAction.CallbackContext context)
    {

        if (context.started && !context.performed)
        {
            //Shoot Here
            Debug.Log("Shoot");
        }

    }

    public void MoveButton(InputAction.CallbackContext context)
    {

        if (context.started && !context.performed)
        {
            //Movement here
            unitSelected.GetComponent<Movement_Controller>().Move();
        }

    }
}

I could be mistaken, but is the issue not that you're registering for the "performed" callbacks, but then in your handlers you're checking "!context.performed"?

I am assuming you are saying this

  • if (context.started && !context.performed)
  • {
  • //Shoot Here
  • Debug.Log("Shoot");
  • }

I put that there because for some reason when I use the component to make the input system work it call the function 4 times. Adding the ifs prevented it.


Ok, but look at how you're preventing it. I strongly suspect you're filtering out the wrong calls.

1 Like

Thanks it works now, it is the if's statements. I think I should not have left them there because I change into the component midway and then came back to try using scripts again. Thanks again!

how we can use the c# generated class this way for local multiplayer?

Each generated class is an IInputActionCollection. Each collection of actions will by default enable all bindings and grab controls from all available devices (i.e. from InputSystem.devices). This behavior can be controlled through two properties: "devices" and "bindingMask".

"devices" controls which devices are used by the actions in that collection. By default it's not set which leads to the "ok, then I'm just going to grab from InputSystem.devices" behavior. You can set this property at any time and repeatedly and the actions will update.

"bindingMask" controls which bindings are used and which are ignored. Only bindings that match the mask will get resolved to controls, the rest will just be skipped. By default, there is no mask which leads to "ok, then I'm just going to enable everything". You can set this property at any time to reflect which bindings you want active at any time.

So.... long story short, you can set up multiplayer by setting these two properties.

// Let's assume you have a generated C# class called "MyControls".
// Let's create two instances of this and set one up for gamepad
// and one up for keyboard&mouse. Let's assume there is a control
// scheme called "Gamepad" and another called "KeyboardMouse".

// P1 gets gamepad.
var p1Controls = new MyControls();
p1Controls.devices = new[] { Gamepad.all[0] };
p1Controls.bindingMask = InputBinding.MaskByGroup("Gamepad");
p1Controls.Enable();

// P2 gets keyboard&mouse.
var p2Controls = new MyControls();
p2Controls.devices = new[] { Keyboard.current, Mouse.current };
p2Controls.bindingMask = InputBinding.MaskByGroup("KeyboardMouse");
p2Controls.Enable();

So with this, you have two independent MyControls input setups.

If you want to take this one step further, you can leave much of the mechanics to InputUser, which is what PlayerInput uses underneath.

// P1 gets gamepad.
var p1Controls = new MyControls();
var p1User = InputUser.PerformPairingWithDevice(gamepad.all[0]);
p1User.AssociateActionsWithUser(p1Controls);
p1User.ActivateControlScheme("Gamepad");
p1Controls.Enable();

// P2 gets keyboard&mouse.
var p2Controls = new MyControls();
var p2User = InputUser.PerformPairingWithDevice(Keyboard.current);
InputUser.PerformPairingWithDevice(Mouse.current, user: p2User);
p2User.AssociateActionsWithUser(p2Controls);
p2User.ActivateControlScheme("KeyboardMouse");
p2Controls.Enable();

But it's not necessary. A working multiplayer setup can be achieved with just the generated C# class alone.

8 Likes

thanks, @Rene-Damm the way of doing using the Player Input manager and manually spawning player on join using the InputUser or Player Input is for me the better option. thanks so much.

The input system is now at version 1.0.0, why do i still have to find some forum post for essential documentation?

Honestly the PlayerInputManager is making things more confusing rather than helping out, i cant understand why it was added. It fits a very specific case, where players join in at any moment and might have split screen, and even that not very well. Any other form of having local multiplayer, such as pre-selecting input devices in a menu, seems utterly unsupported.

16 Likes

Hi, I've just tried to implement your solution and it throws a memory leak error that seems to be coming from the user.ActivateControlScheme("Gamepad"); line: "A native collection has not been disposed, resulting in a memory leak". Is there some extra line to force it to dispose of the native collection?


Oh and if it's of any help the error only registers starting from the second play after compiling the script for whatever reason (even if the script itself wasn't changed), so the first play after returning to unity from visual studio never registers an error.

Hi, I'm trying to figure out how to get this information from the PlayerInput module. Maybe something like this:

// you have a reference to the PlayerInput module called p1Input
var p1Controls = new MyControls();
p1Controls.devices = p1Input.devices;
p1Controls.bindingMask = InputBinding.MaskByGroup("p1Input.controlScheme");

It would be nice if you could just pass an InputControlScheme struct to the script since that would have all the necessary information, right? Or couldn't it just look for a PlayerInput module?


Apologies if I am mistaken. From what I know any MyControls would need to be disposed at the end. so it would be something like p1Controls.Dispose();.
Also always desubscribe all methods from the input actions, then disable the action map before using p1Controls.Dispose().
Though this reply might be too late to help you. It's for any1 else out there that stumbles onto it.