AdditiveLevelsNetworkManager.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. using System.Collections;
  2. using UnityEngine;
  3. using UnityEngine.SceneManagement;
  4. namespace Mirror.Examples.AdditiveLevels
  5. {
  6. [AddComponentMenu("")]
  7. public class AdditiveLevelsNetworkManager : NetworkManager
  8. {
  9. public static new AdditiveLevelsNetworkManager singleton { get; private set; }
  10. /// <summary>
  11. /// Runs on both Server and Client
  12. /// Networking is NOT initialized when this fires
  13. /// </summary>
  14. public override void Awake()
  15. {
  16. base.Awake();
  17. singleton = this;
  18. }
  19. [Header("Additive Scenes - First is start scene")]
  20. [Scene, Tooltip("Add additive scenes here.\nFirst entry will be players' start scene")]
  21. public string[] additiveScenes;
  22. [Header("Fade Control - See child FadeCanvas")]
  23. [Tooltip("Reference to FadeInOut script on child FadeCanvas")]
  24. public FadeInOut fadeInOut;
  25. // This is set true after server loads all subscene instances
  26. bool subscenesLoaded;
  27. // This is managed in LoadAdditive, UnloadAdditive, and checked in OnClientSceneChanged
  28. bool isInTransition;
  29. #region Scene Management
  30. /// <summary>
  31. /// Called on the server when a scene is completed loaded, when the scene load was initiated by the server with ServerChangeScene().
  32. /// </summary>
  33. /// <param name="sceneName">The name of the new scene.</param>
  34. public override void OnServerSceneChanged(string sceneName)
  35. {
  36. // This fires after server fully changes scenes, e.g. offline to online
  37. // If server has just loaded the Container (online) scene, load the subscenes on server
  38. if (sceneName == onlineScene)
  39. StartCoroutine(ServerLoadSubScenes());
  40. }
  41. IEnumerator ServerLoadSubScenes()
  42. {
  43. foreach (string additiveScene in additiveScenes)
  44. yield return SceneManager.LoadSceneAsync(additiveScene, new LoadSceneParameters
  45. {
  46. loadSceneMode = LoadSceneMode.Additive,
  47. localPhysicsMode = LocalPhysicsMode.Physics3D // change this to .Physics2D for a 2D game
  48. });
  49. subscenesLoaded = true;
  50. }
  51. /// <summary>
  52. /// Called from ClientChangeScene immediately before SceneManager.LoadSceneAsync is executed
  53. /// <para>This allows client to do work / cleanup / prep before the scene changes.</para>
  54. /// </summary>
  55. /// <param name="sceneName">Name of the scene that's about to be loaded</param>
  56. /// <param name="sceneOperation">Scene operation that's about to happen</param>
  57. /// <param name="customHandling">true to indicate that scene loading will be handled through overrides</param>
  58. public override void OnClientChangeScene(string sceneName, SceneOperation sceneOperation, bool customHandling)
  59. {
  60. //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} OnClientChangeScene {sceneName} {sceneOperation}");
  61. if (sceneOperation == SceneOperation.UnloadAdditive)
  62. StartCoroutine(UnloadAdditive(sceneName));
  63. if (sceneOperation == SceneOperation.LoadAdditive)
  64. StartCoroutine(LoadAdditive(sceneName));
  65. }
  66. IEnumerator LoadAdditive(string sceneName)
  67. {
  68. isInTransition = true;
  69. // This will return immediately if already faded in
  70. // e.g. by UnloadAdditive or by default startup state
  71. yield return fadeInOut.FadeIn();
  72. // host client is on server...don't load the additive scene again
  73. if (mode == NetworkManagerMode.ClientOnly)
  74. {
  75. // Start loading the additive subscene
  76. loadingSceneAsync = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
  77. while (loadingSceneAsync != null && !loadingSceneAsync.isDone)
  78. yield return null;
  79. }
  80. // Reset these to false when ready to proceed
  81. NetworkClient.isLoadingScene = false;
  82. isInTransition = false;
  83. OnClientSceneChanged();
  84. // Reveal the new scene content.
  85. yield return fadeInOut.FadeOut();
  86. }
  87. IEnumerator UnloadAdditive(string sceneName)
  88. {
  89. isInTransition = true;
  90. // This will return immediately if already faded in
  91. // e.g. by LoadAdditive above or by default startup state.
  92. yield return fadeInOut.FadeIn();
  93. // host client is on server...don't unload the additive scene here.
  94. if (mode == NetworkManagerMode.ClientOnly)
  95. {
  96. yield return SceneManager.UnloadSceneAsync(sceneName);
  97. yield return Resources.UnloadUnusedAssets();
  98. }
  99. // Reset these to false when ready to proceed
  100. NetworkClient.isLoadingScene = false;
  101. isInTransition = false;
  102. OnClientSceneChanged();
  103. // There is no call to FadeOut here on purpose.
  104. // Expectation is that a LoadAdditive or full scene change
  105. // will follow that will call FadeOut after that scene loads.
  106. }
  107. /// <summary>
  108. /// Called on clients when a scene has completed loaded, when the scene load was initiated by the server.
  109. /// <para>Scene changes can cause player objects to be destroyed. The default implementation of OnClientSceneChanged in the NetworkManager is to add a player object for the connection if no player object exists.</para>
  110. /// </summary>
  111. /// <param name="conn">The network connection that the scene change message arrived on.</param>
  112. public override void OnClientSceneChanged()
  113. {
  114. // Only call the base method if not in a transition.
  115. // This will be called from LoadAdditive / UnloadAdditive after setting isInTransition to false
  116. // but will also be called first by Mirror when the scene loading finishes.
  117. if (!isInTransition)
  118. base.OnClientSceneChanged();
  119. }
  120. #endregion
  121. #region Server System Callbacks
  122. /// <summary>
  123. /// Called on the server when a client is ready.
  124. /// <para>The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process.</para>
  125. /// </summary>
  126. /// <param name="conn">Connection from client.</param>
  127. public override void OnServerReady(NetworkConnectionToClient conn)
  128. {
  129. // This fires from a Ready message client sends to server after loading the online scene
  130. base.OnServerReady(conn);
  131. if (conn.identity == null)
  132. StartCoroutine(AddPlayerDelayed(conn));
  133. }
  134. // This delay is mostly for the host player that loads too fast for the
  135. // server to have subscenes async loaded from OnServerSceneChanged ahead of it.
  136. IEnumerator AddPlayerDelayed(NetworkConnectionToClient conn)
  137. {
  138. // Wait for server to async load all subscenes for game instances
  139. while (!subscenesLoaded)
  140. yield return null;
  141. // Send Scene msg to client telling it to load the first additive scene
  142. conn.Send(new SceneMessage { sceneName = additiveScenes[0], sceneOperation = SceneOperation.LoadAdditive, customHandling = true });
  143. // We have Network Start Positions in first additive scene...pick one
  144. Transform start = GetStartPosition();
  145. // Instantiate player as child of start position - this will place it in the additive scene
  146. // This also lets player object "inherit" pos and rot from start position transform
  147. GameObject player = Instantiate(playerPrefab, start);
  148. // now set parent null to get it out from under the Start Position object
  149. player.transform.SetParent(null);
  150. // Wait for end of frame before adding the player to ensure Scene Message goes first
  151. yield return new WaitForEndOfFrame();
  152. // Finally spawn the player object for this connection
  153. NetworkServer.AddPlayerForConnection(conn, player);
  154. }
  155. #endregion
  156. }
  157. }