AdditiveLevelsNetworkManager.cs 7.8 KB

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