Local Multiplayer using Player Input Manager and identifying the players (Complex and thorough)

Click the image to an example of Local multiplayer, note some spawning locations are not functioning in the WebGL version.

First grab a copy of the Unity-InputSystem repository from Github.

Alternatively if you have a Unity project with player movement enabled using the new Input System you can use that.

Adding the Player Input Manager

Create an empty GameObject on the scene.

Call it PlayerManager or similar. This object will be used to manager players joining.

In the inspector for this object, click Add Component and add a Player Input Manager component.

You now have a player input manager component.

You will see that there is currently no Player Prefab selected.

If you haven’t converted your player into a prefab do this now by dragging the object into the Project panel. You’ll know the object is a prefab on the scene if it is blue.

We don’t want to have the player on the scene when the game starts so delete it from the scene.

Adding the Player prefab to the Player Input Manager

Go back to the inspector of the PlayerManager.

Click the target circle next to Player Prefab or drag the player onto the None (Game Object) box.

To find the player, click on the Assets tab and then select your player Prefab

Test your game by clicking the play button.

Note that when the game starts the initial main camera on the scene is used. This is because we haven’t created the camera for the player or the player object yet.

Click a button or press a key to add a player

Note that when you click a button a new player is spawned. We can also see in the heirarchy that a Player(Clone) has been created / instantiated.

Note the player will spawn at the origin point of the scene by default. We will change this later. If we move our character it will clip / pop to the nearest location (Now you know how the speedrunners do this).

Note if we use two or more control systems (keyboards, controllers) multiple players spawn. You can see the players and their clones in the inspector.

Split Screen

At the moment they are all sharing the same camera.

To change this head back to the Player Input Manager and check Enable Split-Screen.

Hopefully when you play the game you will see a split screen view like the one below.

Not Showing Split Screen

If you get a view that changes the camera to the new player when they join make the changes.

Open the player prefab.

Find the Player Input component.

You will probably notice that Camera is set to None (Camera)

Drag on the camera to use for the player or click the target.

Try again and you should have split screen working.

Changing the Spawn Point

By default the player will spawn at the world origin (0,0,0)

We want to change this.

We will write a script to change the spawn point to one of our choosing.

Create some empty GameObjects and place them on the scene. These will be our spawn points.

It is a good idea to nest these inside a parent game object. I have created one called SpawnController. You could also place these on your level.

Empty GameObjects are hard to see.

To make them easy to see select the GameObject and go to the Inspector.

Click the drop down next to the Cube

The GameObject will now be labelled.

Note you can select multiple GameObjects and apply the tags to them at once.

We can now easily see out spawn locations.

The PlayerSpawnManager Script

Now we are ready to create the PlayerSpawnManagerScript.

Create a new C# Script called PlayerSpawnManager and attach it to the PlayerManager object.

Open PlayerSpawnManager in your editor.

Line 4 includes the InputSystem

The Start and Update methods have been removed.

The OnPlayerJoined() method is added.

Add a Debug.Log(“Player Joined”); statement to the method to test the code.

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

public class PlayerSpawnManager : MonoBehaviour
{
    void OnPlayerJoined() {
        Debug.Log("PlayerInput Joined");
    }
}

Press the play button and join a player as usual.

We can see that when a player joins the Log is printed to the console.

Identifying the Player

Now we want to be able to identify the player easily. This guide will just show how to find the playerIndex which is a unique number for the player. It starts at 0 for the first player and increments as players join.

Modify the method constructor to take a PlayerInput playerInput argument.

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

public class PlayerSpawnManager : MonoBehaviour
{
    public GameObject[] spawnLocations;

    void OnPlayerJoined(PlayerInput playerInput) {
        Debug.Log("PlayerInput Joined");
    }
}

Now modify the Debug.Log method on line 11 so that it prints out a message “PlayerInput ID: ” and then the playerIndex.

We can access the index (id) of the player through the PlayerInput object that is created when a player joins.

To do this we use dot notation to access the playerIndex property of playerInput with the code playerInput.playerIndex

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

public class PlayerSpawnManager : MonoBehaviour
{
    void OnPlayerJoined(PlayerInput playerInput) {
        
        Debug.Log("PlayerInput ID: " + playerInput.playerIndex);
        
    }
}

Test and run the program.

You can now see that three players have joined each with the index 0, 1, and 2 respectively in the order that they joined the game.

Modifying Player Details – PlayerDetails Script

Now we want to create a script which will store details about the player.

We will use this to store the player number which can then be shown on the player and als oset the starting spawn location for the player.

Go to the player prefab in Unity.

Open the prefab.

Create a PlayerDetails script and attach it to the prefab.

Open the script in your editor.

Add two variables, both of which are public so they can be accessed from outside the class:

  • an int to store the playerID
  • a Vector3 to store the start spawn position

In this example they are named playerID and startPos respectively.

On line 12 inside the start method we have a line of code transform.position = startPos; this takes the Vector3 value in startPos and sets the transforms position of the object this scripts is attached to (the Player) to that value. This has the effect of creating / instantiating / spawning the player at this location rather than the default world origin of (0,0,0).

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

public class PlayerDetails : MonoBehaviour
{
    public int playerID;
    public Vector3 startPos;
    // Start is called before the first frame update
    void Start()
    {
        transform.position = startPos;
    }
}

However the code won’t move us there yet as we haven’t set the spawn location for the player or players yet.

Spawning at a Specific Location

Go back to the PlayerSpawnManager script.

On Line 8 add in an array to store the spawn locations. public Transform[] spawnLocations;

This will allow us to add the transform of each spawn GameObject.

We don’t want to use a Vector3 as then we have to pass x,y, and z separately. If we pass a GameObject we are passing more information than we need to.

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

public class PlayerSpawnManager : MonoBehaviour
{
    public Transform[] spawnLocations;
    
    void OnPlayerJoined(PlayerInput playerInput) {
        
        Debug.Log("PlayerInput ID: " + playerInput.playerIndex);
        
    }
}

Before working on the code we will switch back to Unity to add in the spawn locations.

In the player spawn manager script on the Player Manager object we can now see the array.

At the moment the array is empty.

Change the 0 to the number of different spawn points you have.

Now we can see the array has four elements.

Add the spawn point game objects by either dragging them onto the location or using the circle.

Save the Scene

Go back to PlayerSpawnManager in your editor of choice.

There are two lines of code we need to add. They are quite long and seem complicated.

The first is to set the playerID on the player.

The second is to set the start position for the player.

playerInput.gameObject.GetComponent<PlayerDetails>() takes the playerInput object that is created when a player joins, gets the gameObject linked to it (which is the player object for that player). We then use the GetComponent<>() method to get the script component on the player we want. Note that inside the <> is the name of the script, in this case PlayerDetails, this is followed by the () of the method.

This exists for both lines of code.

In the first line (18) we have .playerID which will get the playerID variable of the PlayerDetails script. We then assign the playerIndex and add 1 to it.

In the second line (19) we get the start position with .startPos and then assign the spawnLocation position that is stored in the matching index of the spawnLoacations array.

For example the first player has a playerIndex of 0, the second 1, etc. We access that with playerInput.playerIndex, we use this value to get the associated location from the spawnLocations[] array with the playerInput.playerIndex being used as the index value inside the []. We then finally use .position to get the Vector3 of the position we want to spawn the player at.

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

public class PlayerSpawnManager : MonoBehaviour
{
    public Transform[] spawnLocations;
    
    void OnPlayerJoined(PlayerInput playerInput) {
        
        Debug.Log("PlayerInput ID: " + playerInput.playerIndex);
        
        // Set the player ID, add one to the index to start at Player 1
        playerInput.gameObject.GetComponent<PlayerDetails>().playerID = playerInput.playerIndex + 1;

        // Set the start spawn position of the player using the location at the associated element into the array.
        playerInput.gameObject.GetComponent<PlayerDetails>().startPos = spawnLocations[playerInput.playerIndex].position;
    }
}

Save your files and switch back to Unity and test your program.

One Player
Two Players
Three Players

We now have local multiplayer working with players spawning in different locations

You might also like