using System; using System.Collections.Generic; using Unity.Netcode; using UnityEngine; namespace Unity.Multiplayer.Samples.Utilities { /// /// Contains data on scene loading progress for the local instance and remote instances. /// public class LoadingProgressManager : NetworkBehaviour { [SerializeField] GameObject m_ProgressTrackerPrefab; /// /// Dictionary containing references to the NetworkedLoadingProgessTrackers that contain the loading progress of /// each client. Keys are ClientIds. /// public Dictionary ProgressTrackers { get; } = new Dictionary(); /// /// This is the AsyncOperation of the current load operation. This property should be set each time a new /// loading operation begins. /// public AsyncOperation LocalLoadOperation { set { m_IsLoading = true; LocalProgress = 0; m_LocalLoadOperation = value; } } AsyncOperation m_LocalLoadOperation; float m_LocalProgress; bool m_IsLoading; /// /// This event is invoked each time the dictionary of progress trackers is updated (if one is removed or added, for example.) /// public event Action onTrackersUpdated; /// /// The current loading progress for the local client. Handled by a local field if not in a networked session, /// or by a progress tracker from the dictionary. /// public float LocalProgress { get => IsSpawned && ProgressTrackers.ContainsKey(NetworkManager.LocalClientId) ? ProgressTrackers[NetworkManager.LocalClientId].Progress.Value : m_LocalProgress; private set { if (IsSpawned && ProgressTrackers.ContainsKey(NetworkManager.LocalClientId) && ProgressTrackers[NetworkManager.LocalClientId].IsSpawned) { ProgressTrackers[NetworkManager.LocalClientId].Progress.Value = value; } else { m_LocalProgress = value; } } } public override void OnNetworkSpawn() { if (IsServer) { NetworkManager.OnClientConnectedCallback += AddTracker; NetworkManager.OnClientDisconnectCallback += RemoveTracker; AddTracker(NetworkManager.LocalClientId); } } public override void OnNetworkDespawn() { if (IsServer) { NetworkManager.OnClientConnectedCallback -= AddTracker; NetworkManager.OnClientDisconnectCallback -= RemoveTracker; } ProgressTrackers.Clear(); onTrackersUpdated?.Invoke(); } void Update() { if (m_LocalLoadOperation != null && m_IsLoading) { if (m_LocalLoadOperation.isDone) { m_IsLoading = false; LocalProgress = 1; } else { LocalProgress = m_LocalLoadOperation.progress; } } } [Rpc(SendTo.ClientsAndHost)] void ClientUpdateTrackersRpc() { if (!IsHost) { ProgressTrackers.Clear(); foreach (var tracker in FindObjectsOfType()) { // If a tracker is despawned but not destroyed yet, don't add it if (tracker.IsSpawned) { ProgressTrackers[tracker.OwnerClientId] = tracker; if (tracker.OwnerClientId == NetworkManager.LocalClientId) { LocalProgress = Mathf.Max(m_LocalProgress, LocalProgress); } } } } onTrackersUpdated?.Invoke(); } void AddTracker(ulong clientId) { if (IsServer) { var tracker = Instantiate(m_ProgressTrackerPrefab); var networkObject = tracker.GetComponent(); networkObject.SpawnWithOwnership(clientId); ProgressTrackers[clientId] = tracker.GetComponent(); ClientUpdateTrackersRpc(); } } void RemoveTracker(ulong clientId) { if (IsServer) { if (ProgressTrackers.ContainsKey(clientId)) { var tracker = ProgressTrackers[clientId]; ProgressTrackers.Remove(clientId); tracker.NetworkObject.Despawn(); ClientUpdateTrackersRpc(); } } } } }