Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections;
- using System.Collections.Generic;
- using NUnit.Framework.Internal;
- using PurrNet;
- using Unity.Cinemachine;
- using Unity.VisualScripting;
- using UnityEngine;
- using UnityEngine.InputSystem;
- public class PlayerSpawnManager : MonoBehaviour, IProvidePrefabInstantiated
- {
- // ✅ Dynamic list of all active players
- public static PlayerSpawnManager Instance { get; private set; }
- public List<GameObject> AllPlayers { get; private set; } = new List<GameObject>();
- //The reason that "public static PlayerSpawnManager Instance" is neccessary is that enables other files to access AllPlayers by doing PlayerSpawnManager.Instance.AllPlayers, rather than having to do [SerializeField] private PlayerSpawnManager manager;
- private Dictionary<GameObject, int> playerTeams = new Dictionary<GameObject, int>();
- [SerializeField] private GameObject playerPrefab;
- [SerializeField] private GameManager gManager;
- [SerializeField] private Transform[] spawnPoints;
- [SerializeField] private Transform[] respawnPoints;
- [SerializeField] private Transform[] teleportPoints;
- [SerializeField] private bool isCouchCOOP = true;
- [SerializeField] private int maxPlayers = 4;
- [SerializeField] private bool autoDetectControllers = true;
- [SerializeField] private int manualPlayerCount = 2; //network only!
- private bool couchCOOPAudioAlreadyAssigned = false;
- private int nextPlayerIndex = 0;
- private Dictionary<InputDevice, GameObject> deviceToPlayer = new Dictionary<InputDevice, GameObject>();
- [Header("Team Color Materials")]
- [SerializeField] private Material team1Material; //Blue
- [SerializeField] private Material team2Material; //Red
- void Awake() //This generates Instance.AllPlayers before anything happens, which is neccesary so it always exists.
- {
- if (Instance != null && Instance != this) //Is there already a different manager named instance active?
- {
- Destroy(gameObject); //If there’s already another manager, we destroy the entire GameObject this script is attached to (the duplicate)
- return;
- }
- Instance = this; //If there wasn’t an existing different instance, assign the static Instance to this instance. Now other classes can use PlayerSpawnManager.Instance to access it!
- }
- void Start()
- {
- if (isCouchCOOP){
- // Enable all connected displays
- //Todo change to actual split-screen, or add option for both
- for (int i = 0; i < Display.displays.Length; i++)
- {
- Display.displays[i].Activate();
- }
- SpawnCouchCoopPlayers();
- // DISABLE the network PlayerSpawner if it exists
- var playerSpawner = FindAnyObjectByType<PlayerSpawner>();
- if (playerSpawner != null)
- {
- playerSpawner.enabled = false;
- }
- }
- else
- {
- // Network mode - register with PlayerSpawner
- var playerSpawner = FindAnyObjectByType<PlayerSpawner>();
- if (playerSpawner != null)
- {
- playerSpawner.SetPrefabInstantiatedProvider(this);
- }
- }
- }
- // Update is called once per frame
- void Update() //todo Limits game to 1 keyboard!
- {
- // Only allow dynamic joining in couch co-op mode if you want
- if (isCouchCOOP && autoDetectControllers && AllPlayers.Count <= maxPlayers)
- {
- // Check for new keyboard input
- if (Keyboard.current != null && Keyboard.current.spaceKey.wasPressedThisFrame)
- {
- if (!deviceToPlayer.ContainsKey(Keyboard.current))
- {
- SpawnLocalPlayer(Keyboard.current, "Keyboard&Mouse");
- }
- }
- // Check for new gamepad input
- foreach (var gamepad in Gamepad.all)
- {
- if (gamepad.buttonSouth.wasPressedThisFrame)
- {
- if (!deviceToPlayer.ContainsKey(gamepad))
- {
- SpawnLocalPlayer(gamepad, "Gamepad");
- }
- }
- }
- }
- //By pressing L, counts number of players and what player number they are
- if (Keyboard.current.lKey.wasPressedThisFrame)
- {
- Debug.Log("=== Current Players in AllPlayers List ===");
- for (int i = 0; i < AllPlayers.Count; i++)
- {
- GameObject player = AllPlayers[i];
- Debug.Log($"Player {i}: {player.name}");
- }
- Debug.Log($"Total players: {AllPlayers.Count}");
- }
- if (Keyboard.current.mKey.wasPressedThisFrame)
- {
- Debug.Log("RESTART");
- gManager.RestartGame();
- }
- }
- void SpawnCouchCoopPlayers()
- {
- int playerCount = 0;
- if (autoDetectControllers)
- {
- // Count connected input devices
- List<InputDevice> devices = new List<InputDevice>();
- // Add keyboard if present
- if (Keyboard.current != null)
- {
- devices.Add(Keyboard.current);
- }
- // Add all gamepads
- foreach (var gamepad in Gamepad.all)
- {
- devices.Add(gamepad);
- }
- playerCount = Mathf.Min(devices.Count, maxPlayers);
- // Spawn a player for each device
- for (int i = 0; i < playerCount; i++)
- {
- InputDevice device = devices[i];
- string controlScheme = (device is Keyboard) ? "Keyboard&Mouse" : "Gamepad";
- SpawnLocalPlayer(device, controlScheme);
- }
- }
- else
- {
- // Use manual player count
- playerCount = Mathf.Min(manualPlayerCount, maxPlayers);
- // Spawn players with available devices
- List<InputDevice> availableDevices = new List<InputDevice>();
- if (Keyboard.current != null)
- availableDevices.Add(Keyboard.current);
- foreach (var gamepad in Gamepad.all)
- availableDevices.Add(gamepad);
- for (int i = 0; i < playerCount; i++)
- {
- if (i < availableDevices.Count)
- {
- InputDevice device = availableDevices[i];
- string controlScheme = (device is Keyboard) ? "Keyboard&Mouse" : "Gamepad";
- SpawnLocalPlayer(device, controlScheme);
- }
- else
- {
- // Not enough controllers, spawn without input
- Debug.LogWarning($"Player {i + 1} spawned without controller (not enough devices)");
- SpawnPlayerWithoutInput(i);
- }
- }
- }
- Debug.Log($"Spawned {playerCount} couch co-op players");
- }
- void SpawnLocalPlayer(InputDevice device, string controlScheme)
- {
- var player = PlayerInput.Instantiate(playerPrefab, controlScheme: controlScheme, pairWithDevice: device);
- deviceToPlayer[device] = player.gameObject;
- SetupPlayer(player.gameObject, nextPlayerIndex, controlScheme);
- }
- void SpawnPlayerWithoutInput(int playerIndex)
- {
- GameObject player = Instantiate(playerPrefab);
- SetupPlayer(player, playerIndex, "NoInput");
- }
- private void SetupPlayer(GameObject player, int playerIndex, string controlScheme)
- {
- // Add to tracking
- AllPlayers.Add(player);
- // Set spawn position
- if (spawnPoints.Length > 0)
- {
- int spawnIndex = nextPlayerIndex % spawnPoints.Length;
- player.transform.SetPositionAndRotation(spawnPoints[spawnIndex].position,spawnPoints[spawnIndex].rotation);
- }
- // Set player index
- var pHealth = player.GetComponent<PlayerHealth>();
- if (pHealth != null)
- {
- pHealth.PlayerIndex = playerIndex;
- GameManager.Instance.UpdateLives(pHealth.PlayerIndex, pHealth.lives);
- //Gives GameManager lives, and tells it that new player has joined, which is then given to pHealth's RefreshHearts
- pHealth.SetSpawnManager(this);
- }
- // Assign team & color
- int team = (AllPlayers.Count % 2 == 1) ? 1 : 2;
- playerTeams[player] = team;
- AssignTeamMaterial(player, team);
- //Assign name
- player.name = $"P{playerIndex + 1}_{controlScheme}";
- // Setup UI
- var invUI = player.GetComponent<InventoryUI>();
- if (invUI != null && controlScheme == "Gamepad")
- {
- // Check if this is a gamepad player somehow
- invUI.OnControllerUI();
- }
- // Configure camera and layers
- var cam = player.GetComponentInChildren<CinemachineCamera>();
- if (cam != null)
- {
- Camera actualCamera = cam.GetComponentInChildren<Camera>();
- if (isCouchCOOP){
- actualCamera.targetDisplay = playerIndex;}
- ConfigureCamera(actualCamera, playerIndex);
- /*
- var colorScript = actualCamera.GetComponent<PerCameraTeamColors>(); //failed attempt at using a different method for team colors
- if (colorScript != null)
- {
- colorScript.myTeam = team;
- }*/
- AssignPlayerLayers(player.gameObject, nextPlayerIndex);
- // Add audio listener if needed
- if (actualCamera.GetComponent<AudioListener>() == null && !isCouchCOOP){
- actualCamera.gameObject.AddComponent<AudioListener>();}
- if (actualCamera.GetComponent<AudioListener>() == null && isCouchCOOP && !couchCOOPAudioAlreadyAssigned){
- actualCamera.gameObject.AddComponent<AudioListener>();
- couchCOOPAudioAlreadyAssigned = true;}
- }
- Debug.Log($"Player {playerIndex + 1} ({controlScheme}) setup complete");
- nextPlayerIndex++;
- }
- void AssignPlayerLayers(GameObject player, int index)
- {
- Transform fp = player.transform.Find("FirstPerson");
- Transform tp = player.transform.Find("ThirdPerson");
- if (fp != null && tp != null)
- {
- int fpLayer = LayerMask.NameToLayer($"P{index + 1}_FirstPerson");
- int tpLayer = LayerMask.NameToLayer($"P{index + 1}_ThirdPerson");
- SetLayerRecursively(fp.gameObject, fpLayer);//Takes the FirstPerson root GameObject, Sets its layer and all children’s layers to fpLayer
- SetLayerRecursively(tp.gameObject, tpLayer);
- }
- }
- void SetLayerRecursively(GameObject obj, int layer)
- {
- obj.layer = layer;
- foreach (Transform child in obj.transform)
- SetLayerRecursively(child.gameObject, layer);
- }
- void ConfigureCamera(Camera cam, int playerIndex)
- {
- int mask = 0;
- // Add common layers everyone can see
- mask |= 1 << LayerMask.NameToLayer("Default");
- mask |= 1 << LayerMask.NameToLayer("Ground");
- mask |= 1 << LayerMask.NameToLayer("UI");
- mask |= 1 << LayerMask.NameToLayer("Grenade");
- mask |= 1 << LayerMask.NameToLayer("NoCollideWithGrenades");
- // Add this player's own first-person body
- string ownFPLayer = $"P{playerIndex + 1}_FirstPerson";
- mask |= 1 << LayerMask.NameToLayer(ownFPLayer);
- // Add this player's own UI
- string ownUILayer = $"UI_Player{playerIndex + 1}";
- mask |= 1 << LayerMask.NameToLayer(ownUILayer);
- // Add third-person bodies of ALL other players (P1-P4)
- for (int i = 0; i < maxPlayers; i++)
- {
- if (i != playerIndex) // Don't add our own third-person body
- {
- string otherTPLayer = $"P{i + 1}_ThirdPerson";
- mask |= 1 << LayerMask.NameToLayer(otherTPLayer);
- }
- }
- // Explicitly hide other players' UI layers
- for (int i = 0; i < maxPlayers; i++)
- {
- if (i != playerIndex)
- {
- string otherUILayer = $"UI_Player{i + 1}";
- mask &= ~(1 << LayerMask.NameToLayer(otherUILayer));
- }
- }
- cam.cullingMask = mask;
- }
- public static void SetPlayerHeavenVisibility(GameObject player, bool canSee)
- {
- var cam = player.GetComponentInChildren<Camera>();
- if (cam == null) return;
- int heavenLayer = LayerMask.NameToLayer("HeavenStuff");
- if (canSee)
- cam.cullingMask |= (1 << heavenLayer);
- else
- cam.cullingMask &= ~(1 << heavenLayer);
- }
- public void RespawnPlayer(GameObject player) //Teleports them into heaven
- {
- int playerIndex = AllPlayers.IndexOf(player);
- if (playerIndex >= 0 && respawnPoints.Length > 0)
- {
- int spawnIndex = playerIndex % respawnPoints.Length;
- player.transform.position = respawnPoints[spawnIndex].position;
- player.transform.rotation = respawnPoints[spawnIndex].rotation;
- }
- }
- void AssignTeamMaterial(GameObject player, int team)
- {
- Material teamMaterial = (team == 1) ? team1Material : team2Material;
- // Find all renderers in FirstPerson and ThirdPerson
- Transform firstPerson = player.transform.Find("FirstPerson");
- Transform thirdPerson = player.transform.Find("ThirdPerson");
- if (firstPerson != null)
- {
- foreach (Renderer renderer in firstPerson.GetComponentsInChildren<Renderer>()){
- renderer.material = teamMaterial;}
- }
- if (thirdPerson != null)
- {
- foreach (Renderer renderer in thirdPerson.GetComponentsInChildren<Renderer>()){
- renderer.material = teamMaterial;}
- }
- }
- // public method to get a player's team
- public int GetPlayerTeam(GameObject player)
- {
- if (playerTeams.ContainsKey(player)){
- return playerTeams[player];} //returns 1 or 2
- return -1; // Player not found
- }
- public void RemovePlayer(GameObject player)
- {
- AllPlayers.Remove(player);
- playerTeams.Remove(player);
- // Remove from device tracking
- InputDevice deviceToRemove = null;
- foreach (var kvp in deviceToPlayer)
- {
- if (kvp.Value == player)
- {
- deviceToRemove = kvp.Key;
- break;
- }
- }
- if (deviceToRemove != null)
- {
- deviceToPlayer.Remove(deviceToRemove);
- }
- }
- // This is called by PlayerSpawner in network mode
- public void OnPrefabInstantiated(GameObject prefabInstance, PlayerID player, SceneID scene)
- {
- if (!isCouchCOOP)
- {
- SetupPlayer(prefabInstance, AllPlayers.Count, "Network"); //use count as player index
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment