Hello everyone,
I’m working on a local multiplayer game and I encounter some issue with the control of the player.
I have two scene :
1°) Players press a button on their controller to spawn on the screen. And they choose a team to play with. There are 4 teams to choose from. And when all players are ready, a second scene is loaded.
2°) The players spawn at different position regarding their team.
If the first player is keyboard controlled and the second is a gamepad controlled, after the loading of the second scene, player 1 is still controlled by the keyboard and player 2 is still controled by the gamepad.
Now lets try if the keyboard is NOT the player 1 :
a) In the first scene, if the player 1 is controlled by a gamepad and the second player by the keyboard, when the second scene is loaded, the first player is now controlled by the keyboard and the second player is now controlled by the gamepad.
And in the Input player component, I can actually see that now the keyboard controls player 1 and the joystick controls player 2. Whereas in the team selection scene it was the other way round.
b) If in the first scene the player 1 is controlled by a gamepad and the second player is controller by a second gamepad. When the second scene has loaded, the first player can be controlled by the first gamepad but the second player doesn’t move at all. Because in his player Input Player component I see that player2 is waiting for keyboard input (but as any keyboard was press in scene 1, if I press on my keyboard, the player 2 doesn’t move. So it wait for a not existing keyboard …
c) If I use the keyboard for player one, and gamepad for player 2 and gamepad for player 3, all is working very well.
I don’t know if it is my script that lead to this issue or if there is something I don’t get with Unity so here is my scripts :
PlayerConfigurationManager.cs is called everytime a player join (by pressing a controller buton) in the first scene (where players has to choose a team) and the function “HandlePlayerJoin” is used.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.InputSystem;
using System.Linq;
using TMPro;
using UnityEngine.SceneManagement;
using UnityEngine;
public class PlayerConfigurationManager : MonoBehaviour
{
private List<PlayerConfiguration> _playerConfigs;
[SerializeField] private int _maxPlayer = 4;
[SerializeField] private int _minPlayer = 2;
[SerializeField] private TextMeshProUGUI _textNumberOfPlayer;
[SerializeField] private TextMeshProUGUI _textNumberOfJoinedPlayer;
public static PlayerConfigurationManager Instance { get; private set; }
private void Awake()
{
if (Instance != null)
{
Debug.Log("SINGLETON : Trying to create another instance of singleton!");
}
else
{
Instance = this;
_playerConfigs = new List<PlayerConfiguration>();
}
}
public void SetPlayerColor (int index, Material color)
{
_playerConfigs[index].PlayerMaterial = color;
}
public void SetTeam(int index, int teamNumber)
{
_playerConfigs[index].Team = teamNumber;
}
public void ReadyPlayer(int index)
{
_playerConfigs[index].IsReady = true;
if (_playerConfigs.Count >= _minPlayer && _playerConfigs.All(p => p.IsReady == true))
{
SceneManager.LoadScene("TopDownScene");
}
}
public void UnreadyPlayer(int index)
{
_playerConfigs[index].IsReady = false;
}
public void HandlePlayerJoin(UnityEngine.InputSystem.PlayerInput pi)
{
if(!_playerConfigs.Any(p => p.PlayerIndex == pi.playerIndex))
{
pi.transform.SetParent(transform);
_playerConfigs.Add( new PlayerConfiguration(pi));
_textNumberOfPlayer.GetComponent<PlayerNumberReadyTextScript>().SetPlayerCount(_playerConfigs.Count);
_textNumberOfJoinedPlayer.GetComponent<TextToJoinScript>().SetJoinedPlayerCount(_playerConfigs.Count);
}
}
public List<PlayerConfiguration> GetPlayerConfigs()
{
return _playerConfigs;
}
private void Update()
{
}
}
public class PlayerConfiguration
{
public PlayerConfiguration(UnityEngine.InputSystem.PlayerInput pi)
{
PlayerIndex = pi.playerIndex;
Input = pi;
}
public UnityEngine.InputSystem.PlayerInput Input { get; set; }
public int PlayerIndex { get; set; }
public bool IsReady { get; set; }
public Material PlayerMaterial { get; set; }
public int Team { get; set; }
}
In second scene, I have the InitializeLevel.cs. This script make the players spwan at different position regarding their team.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class InitializeLevel : MonoBehaviour
{
[SerializeField] private Transform[] _playerSpawns;
private int _offSet1 = 0;
private int _offSet2 = 0;
private int _offSet3 = 0;
private int _offSet4 = 0;
private Vector3 _OffSetVector = new Vector3(3.0f, 0f, 0f);
[SerializeField] private GameObject _playerPrefab;
// Start is called before the first frame update
void Start()
{
var playerConfigs = PlayerConfigurationManager.Instance.GetPlayerConfigs().ToArray();
for (int i = 0; i < playerConfigs.Length; i++)
{
Debug.Log("Player Index : " + playerConfigs[i].PlayerIndex + "is from Team : " + playerConfigs[i].Team);
switch (playerConfigs[i].Team)
{
case 1:
_offSet1++;
var player1 = Instantiate(_playerPrefab, _playerSpawns[playerConfigs[i].Team-1].position + _offSet1 * _OffSetVector, Quaternion.identity, gameObject.transform);
player1.GetComponent<PlayerInputHandler>().InitializePlayer(playerConfigs[i]);
break;
case 2:
_offSet2++;
var player2 = Instantiate(_playerPrefab, _playerSpawns[playerConfigs[i].Team-1].position + _offSet2 * _OffSetVector, Quaternion.identity, gameObject.transform);
player2.GetComponent<PlayerInputHandler>().InitializePlayer(playerConfigs[i]);
break;
case 3:
_offSet3++;
var player3 = Instantiate(_playerPrefab, _playerSpawns[playerConfigs[i].Team-1].position + _offSet3 * _OffSetVector, Quaternion.identity, gameObject.transform);
player3.GetComponent<PlayerInputHandler>().InitializePlayer(playerConfigs[i]);
break;
case 4:
_offSet4++;
var player4 = Instantiate(_playerPrefab, _playerSpawns[playerConfigs[i].Team-1].position + _offSet4 * _OffSetVector, Quaternion.identity, gameObject.transform);
player4.GetComponent<PlayerInputHandler>().InitializePlayer(playerConfigs[i]);
break;
}
}
}
}
And each player have the script “PlayerInputHandler.cs”. This script links the correct information between the player and their character (in-game).So therefore correctly distributes the information contained in the playersConfigs.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerInputHandler : MonoBehaviour
{
private PlayerConfiguration _playerConfig;
private CharacterStateMachine _characterStateMachine;
[SerializeField] private GameObject _playerMesh;
private Player_Input _controls;
private void Awake()
{
_characterStateMachine = GetComponent<CharacterStateMachine>();
_controls = new Player_Input();
}
public void InitializePlayer(PlayerConfiguration pc)
{
_playerConfig = pc;
_playerMesh.GetComponentInChildren<Renderer>().material = pc.PlayerMaterial;
_playerConfig.Input.onActionTriggered += Input_OnActionTriggered;
}
private void Input_OnActionTriggered(InputAction.CallbackContext obj)
{
if (obj.action.name == _controls.CharaControls.Move.name)
{
OnMove(obj);
}
}
public void OnMove(InputAction.CallbackContext context)
{
if (_characterStateMachine != null)
{
_characterStateMachine.OnMovementInput(context);
}
}
}
It seems that unity NEEDS a player controlled by a keyboard and it NEEDS to be the player 1 (index 0) : "
The Keyboard Tirannie
"
Can someone help me please ?