using System; using System.Collections; using System.Collections.Generic; using Unity.Netcode; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.UI; namespace Unity.Multiplayer.Samples.Utilities { /// /// This script handles the use of a loading screen with a progress bar and the name of the loaded scene shown. It /// must be started and stopped from outside this script. It also allows updating the loading screen when a new /// loading operation starts before the loading screen is stopped. /// public class ClientLoadingScreen : MonoBehaviour { protected class LoadingProgressBar { public Slider ProgressBar { get; set; } public Text NameText { get; set; } public LoadingProgressBar(Slider otherPlayerProgressBar, Text otherPlayerNameText) { ProgressBar = otherPlayerProgressBar; NameText = otherPlayerNameText; } public void UpdateProgress(float value, float newValue) { ProgressBar.value = newValue; } } [SerializeField] CanvasGroup m_CanvasGroup; [SerializeField] float m_DelayBeforeFadeOut = 0.5f; [SerializeField] float m_FadeOutDuration = 0.1f; [SerializeField] Slider m_ProgressBar; [SerializeField] Text m_SceneName; [SerializeField] List m_OtherPlayersProgressBars; [SerializeField] List m_OtherPlayerNamesTexts; [SerializeField] protected LoadingProgressManager m_LoadingProgressManager; protected Dictionary m_LoadingProgressBars = new Dictionary(); bool m_LoadingScreenRunning; Coroutine m_FadeOutCoroutine; void Awake() { DontDestroyOnLoad(this); Assert.AreEqual(m_OtherPlayersProgressBars.Count, m_OtherPlayerNamesTexts.Count, "There should be the same number of progress bars and name labels"); } void Start() { SetCanvasVisibility(false); m_LoadingProgressManager.onTrackersUpdated += OnProgressTrackersUpdated; } void OnDestroy() { if (m_LoadingProgressManager != null) { m_LoadingProgressManager.onTrackersUpdated -= OnProgressTrackersUpdated; } } void Update() { if (m_LoadingScreenRunning) { m_ProgressBar.value = m_LoadingProgressManager.LocalProgress; } } void OnProgressTrackersUpdated() { // deactivate progress bars of clients that are no longer tracked var clientIdsToRemove = new List(); foreach (var clientId in m_LoadingProgressBars.Keys) { if (!m_LoadingProgressManager.ProgressTrackers.ContainsKey(clientId)) { clientIdsToRemove.Add(clientId); } } foreach (var clientId in clientIdsToRemove) { RemoveOtherPlayerProgressBar(clientId); } // Add progress bars for clients that are now tracked foreach (var progressTracker in m_LoadingProgressManager.ProgressTrackers) { var clientId = progressTracker.Key; if (clientId != NetworkManager.Singleton.LocalClientId && !m_LoadingProgressBars.ContainsKey(clientId)) { AddOtherPlayerProgressBar(clientId, progressTracker.Value); } } } public void StopLoadingScreen() { if (m_LoadingScreenRunning) { if (m_FadeOutCoroutine != null) { StopCoroutine(m_FadeOutCoroutine); } m_FadeOutCoroutine = StartCoroutine(FadeOutCoroutine()); } } public void StartLoadingScreen(string sceneName) { SetCanvasVisibility(true); m_LoadingScreenRunning = true; UpdateLoadingScreen(sceneName); ReinitializeProgressBars(); } void ReinitializeProgressBars() { // deactivate progress bars of clients that are no longer tracked var clientIdsToRemove = new List(); foreach (var clientId in m_LoadingProgressBars.Keys) { if (!m_LoadingProgressManager.ProgressTrackers.ContainsKey(clientId)) { clientIdsToRemove.Add(clientId); } } foreach (var clientId in clientIdsToRemove) { RemoveOtherPlayerProgressBar(clientId); } for (var i = 0; i < m_OtherPlayersProgressBars.Count; i++) { m_OtherPlayersProgressBars[i].gameObject.SetActive(false); m_OtherPlayerNamesTexts[i].gameObject.SetActive(false); } var index = 0; foreach (var progressTracker in m_LoadingProgressManager.ProgressTrackers) { var clientId = progressTracker.Key; if (clientId != NetworkManager.Singleton.LocalClientId) { UpdateOtherPlayerProgressBar(clientId, index++); } } } protected virtual void UpdateOtherPlayerProgressBar(ulong clientId, int progressBarIndex) { m_LoadingProgressBars[clientId].ProgressBar = m_OtherPlayersProgressBars[progressBarIndex]; m_LoadingProgressBars[clientId].ProgressBar.gameObject.SetActive(true); m_LoadingProgressBars[clientId].NameText = m_OtherPlayerNamesTexts[progressBarIndex]; m_LoadingProgressBars[clientId].NameText.gameObject.SetActive(true); } protected virtual void AddOtherPlayerProgressBar(ulong clientId, NetworkedLoadingProgressTracker progressTracker) { if (m_LoadingProgressBars.Count < m_OtherPlayersProgressBars.Count && m_LoadingProgressBars.Count < m_OtherPlayerNamesTexts.Count) { var index = m_LoadingProgressBars.Count; m_LoadingProgressBars[clientId] = new LoadingProgressBar(m_OtherPlayersProgressBars[index], m_OtherPlayerNamesTexts[index]); progressTracker.Progress.OnValueChanged += m_LoadingProgressBars[clientId].UpdateProgress; m_LoadingProgressBars[clientId].ProgressBar.value = progressTracker.Progress.Value; m_LoadingProgressBars[clientId].ProgressBar.gameObject.SetActive(true); m_LoadingProgressBars[clientId].NameText.gameObject.SetActive(true); m_LoadingProgressBars[clientId].NameText.text = $"Client {clientId}"; } else { throw new Exception("There are not enough progress bars to track the progress of all the players."); } } void RemoveOtherPlayerProgressBar(ulong clientId, NetworkedLoadingProgressTracker progressTracker = null) { if (progressTracker != null) { progressTracker.Progress.OnValueChanged -= m_LoadingProgressBars[clientId].UpdateProgress; } m_LoadingProgressBars[clientId].ProgressBar.gameObject.SetActive(false); m_LoadingProgressBars[clientId].NameText.gameObject.SetActive(false); m_LoadingProgressBars.Remove(clientId); } public void UpdateLoadingScreen(string sceneName) { if (m_LoadingScreenRunning) { m_SceneName.text = sceneName; if (m_FadeOutCoroutine != null) { StopCoroutine(m_FadeOutCoroutine); } } } void SetCanvasVisibility(bool visible) { m_CanvasGroup.alpha = visible ? 1 : 0; m_CanvasGroup.blocksRaycasts = visible; } IEnumerator FadeOutCoroutine() { yield return new WaitForSeconds(m_DelayBeforeFadeOut); m_LoadingScreenRunning = false; float currentTime = 0; while (currentTime < m_FadeOutDuration) { m_CanvasGroup.alpha = Mathf.Lerp(1, 0, currentTime / m_FadeOutDuration); yield return null; currentTime += Time.deltaTime; } SetCanvasVisibility(false); } } }