using FishNet.Connection; using FishNet.Managing; using FishNet.Object; using System; using UnityEngine; using UnityEngine.Serialization; namespace FishNet.Component.Spawning { /// /// Spawns a player object for clients when they connect. /// Must be placed on or beneath the NetworkManager object. /// [AddComponentMenu("FishNet/Component/PlayerSpawner")] public class PlayerSpawner : MonoBehaviour { #region Public. /// /// Called on the server when a player is spawned. /// public event Action OnSpawned; #endregion #region Serialized. /// /// Prefab to spawn for the player. /// [Tooltip("Prefab to spawn for the player.")] [SerializeField] private NetworkObject _playerPrefab; /// /// True to add player to the active scene when no global scenes are specified through the SceneManager. /// [Tooltip("True to add player to the active scene when no global scenes are specified through the SceneManager.")] [SerializeField] private bool _addToDefaultScene = true; /// /// Areas in which players may spawn. /// [Tooltip("Areas in which players may spawn.")] [FormerlySerializedAs("_spawns")] public Transform[] Spawns = new Transform[0]; #endregion #region Private. /// /// NetworkManager on this object or within this objects parents. /// private NetworkManager _networkManager; /// /// Next spawns to use. /// private int _nextSpawn; #endregion private void Start() { InitializeOnce(); } private void OnDestroy() { if (_networkManager != null) _networkManager.SceneManager.OnClientLoadedStartScenes -= SceneManager_OnClientLoadedStartScenes; } /// /// Initializes this script for use. /// private void InitializeOnce() { _networkManager = InstanceFinder.NetworkManager; if (_networkManager == null) { Debug.LogWarning($"PlayerSpawner on {gameObject.name} cannot work as NetworkManager wasn't found on this object or within parent objects."); return; } _networkManager.SceneManager.OnClientLoadedStartScenes += SceneManager_OnClientLoadedStartScenes; } /// /// Called when a client loads initial scenes after connecting. /// private void SceneManager_OnClientLoadedStartScenes(NetworkConnection conn, bool asServer) { if (!asServer) return; if (_playerPrefab == null) { Debug.LogWarning($"Player prefab is empty and cannot be spawned for connection {conn.ClientId}."); return; } Vector3 position; Quaternion rotation; SetSpawn(_playerPrefab.transform, out position, out rotation); NetworkObject nob = _networkManager.GetPooledInstantiated(_playerPrefab, true); nob.transform.SetPositionAndRotation(position, rotation); _networkManager.ServerManager.Spawn(nob, conn); //If there are no global scenes if (_addToDefaultScene) _networkManager.SceneManager.AddOwnerToDefaultScene(nob); OnSpawned?.Invoke(nob); } /// /// Sets a spawn position and rotation. /// /// /// private void SetSpawn(Transform prefab, out Vector3 pos, out Quaternion rot) { //No spawns specified. if (Spawns.Length == 0) { SetSpawnUsingPrefab(prefab, out pos, out rot); return; } Transform result = Spawns[_nextSpawn]; if (result == null) { SetSpawnUsingPrefab(prefab, out pos, out rot); } else { pos = result.position; rot = result.rotation; } //Increase next spawn and reset if needed. _nextSpawn++; if (_nextSpawn >= Spawns.Length) _nextSpawn = 0; } /// /// Sets spawn using values from prefab. /// /// /// /// private void SetSpawnUsingPrefab(Transform prefab, out Vector3 pos, out Quaternion rot) { pos = prefab.position; rot = prefab.rotation; } } }