init
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
public class AdditiveLevelsNetworkManager : NetworkManager
|
||||
{
|
||||
[Header("Additive Scenes - First is start scene")]
|
||||
|
||||
[Scene, Tooltip("Add additive scenes here.\nFirst entry will be players' start scene")]
|
||||
public string[] additiveScenes;
|
||||
|
||||
[Header("Fade Control - See child FadeCanvas")]
|
||||
|
||||
[Tooltip("Reference to FadeInOut script on child FadeCanvas")]
|
||||
public FadeInOut fadeInOut;
|
||||
|
||||
// This is set true after server loads all subscene instances
|
||||
bool subscenesLoaded;
|
||||
|
||||
// This is managed in LoadAdditive, UnloadAdditive, and checked in OnClientSceneChanged
|
||||
bool isInTransition;
|
||||
|
||||
#region Scene Management
|
||||
|
||||
/// <summary>
|
||||
/// Called on the server when a scene is completed loaded, when the scene load was initiated by the server with ServerChangeScene().
|
||||
/// </summary>
|
||||
/// <param name="sceneName">The name of the new scene.</param>
|
||||
public override void OnServerSceneChanged(string sceneName)
|
||||
{
|
||||
// This fires after server fully changes scenes, e.g. offline to online
|
||||
// If server has just loaded the Container (online) scene, load the subscenes on server
|
||||
if (sceneName == onlineScene)
|
||||
StartCoroutine(ServerLoadSubScenes());
|
||||
}
|
||||
|
||||
IEnumerator ServerLoadSubScenes()
|
||||
{
|
||||
foreach (string additiveScene in additiveScenes)
|
||||
yield return SceneManager.LoadSceneAsync(additiveScene, new LoadSceneParameters
|
||||
{
|
||||
loadSceneMode = LoadSceneMode.Additive,
|
||||
localPhysicsMode = LocalPhysicsMode.Physics3D // change this to .Physics2D for a 2D game
|
||||
});
|
||||
|
||||
subscenesLoaded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from ClientChangeScene immediately before SceneManager.LoadSceneAsync is executed
|
||||
/// <para>This allows client to do work / cleanup / prep before the scene changes.</para>
|
||||
/// </summary>
|
||||
/// <param name="sceneName">Name of the scene that's about to be loaded</param>
|
||||
/// <param name="sceneOperation">Scene operation that's about to happen</param>
|
||||
/// <param name="customHandling">true to indicate that scene loading will be handled through overrides</param>
|
||||
public override void OnClientChangeScene(string sceneName, SceneOperation sceneOperation, bool customHandling)
|
||||
{
|
||||
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} OnClientChangeScene {sceneName} {sceneOperation}");
|
||||
|
||||
if (sceneOperation == SceneOperation.UnloadAdditive)
|
||||
StartCoroutine(UnloadAdditive(sceneName));
|
||||
|
||||
if (sceneOperation == SceneOperation.LoadAdditive)
|
||||
StartCoroutine(LoadAdditive(sceneName));
|
||||
}
|
||||
|
||||
IEnumerator LoadAdditive(string sceneName)
|
||||
{
|
||||
isInTransition = true;
|
||||
|
||||
// This will return immediately if already faded in
|
||||
// e.g. by UnloadAdditive above or by default startup state
|
||||
yield return fadeInOut.FadeIn();
|
||||
|
||||
// host client is on server...don't load the additive scene again
|
||||
if (mode == NetworkManagerMode.ClientOnly)
|
||||
{
|
||||
// Start loading the additive subscene
|
||||
loadingSceneAsync = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
|
||||
|
||||
while (loadingSceneAsync != null && !loadingSceneAsync.isDone)
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Reset these to false when ready to proceed
|
||||
NetworkClient.isLoadingScene = false;
|
||||
isInTransition = false;
|
||||
|
||||
OnClientSceneChanged();
|
||||
|
||||
yield return fadeInOut.FadeOut();
|
||||
}
|
||||
|
||||
IEnumerator UnloadAdditive(string sceneName)
|
||||
{
|
||||
isInTransition = true;
|
||||
|
||||
// This will return immediately if already faded in
|
||||
// e.g. by LoadAdditive above or by default startup state
|
||||
yield return fadeInOut.FadeIn();
|
||||
|
||||
if (mode == NetworkManagerMode.ClientOnly)
|
||||
{
|
||||
yield return SceneManager.UnloadSceneAsync(sceneName);
|
||||
yield return Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
// Reset these to false when ready to proceed
|
||||
NetworkClient.isLoadingScene = false;
|
||||
isInTransition = false;
|
||||
|
||||
OnClientSceneChanged();
|
||||
|
||||
// There is no call to FadeOut here on purpose.
|
||||
// Expectation is that a LoadAdditive will follow
|
||||
// that will call FadeOut after that scene loads.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on clients when a scene has completed loaded, when the scene load was initiated by the server.
|
||||
/// <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>
|
||||
/// </summary>
|
||||
/// <param name="conn">The network connection that the scene change message arrived on.</param>
|
||||
public override void OnClientSceneChanged()
|
||||
{
|
||||
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} OnClientSceneChanged {isInTransition}");
|
||||
|
||||
// Only call the base method if not in a transition.
|
||||
// This will be called from DoTransition after setting doingTransition to false
|
||||
// but will also be called first by Mirror when the scene loading finishes.
|
||||
if (!isInTransition)
|
||||
base.OnClientSceneChanged();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Server System Callbacks
|
||||
|
||||
/// <summary>
|
||||
/// Called on the server when a client is ready.
|
||||
/// <para>The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process.</para>
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection from client.</param>
|
||||
public override void OnServerReady(NetworkConnectionToClient conn)
|
||||
{
|
||||
//Debug.Log($"OnServerReady {conn} {conn.identity}");
|
||||
|
||||
// This fires from a Ready message client sends to server after loading the online scene
|
||||
base.OnServerReady(conn);
|
||||
|
||||
if (conn.identity == null)
|
||||
StartCoroutine(AddPlayerDelayed(conn));
|
||||
}
|
||||
|
||||
// This delay is mostly for the host player that loads too fast for the
|
||||
// server to have subscenes async loaded from OnServerSceneChanged ahead of it.
|
||||
IEnumerator AddPlayerDelayed(NetworkConnectionToClient conn)
|
||||
{
|
||||
// Wait for server to async load all subscenes for game instances
|
||||
while (!subscenesLoaded)
|
||||
yield return null;
|
||||
|
||||
// Send Scene msg to client telling it to load the first additive scene
|
||||
conn.Send(new SceneMessage { sceneName = additiveScenes[0], sceneOperation = SceneOperation.LoadAdditive, customHandling = true });
|
||||
|
||||
// We have Network Start Positions in first additive scene...pick one
|
||||
Transform start = GetStartPosition();
|
||||
|
||||
// Instantiate player as child of start position - this will place it in the additive scene
|
||||
// This also lets player object "inherit" pos and rot from start position transform
|
||||
GameObject player = Instantiate(playerPrefab, start);
|
||||
// now set parent null to get it out from under the Start Position object
|
||||
player.transform.SetParent(null);
|
||||
|
||||
// Wait for end of frame before adding the player to ensure Scene Message goes first
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
// Finally spawn the player object for this connection
|
||||
NetworkServer.AddPlayerForConnection(conn, player);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01eafa8309a0894479f0f87ae1a9c30f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
60
Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs
Normal file
60
Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
public class FadeInOut : MonoBehaviour
|
||||
{
|
||||
// set these in the inspector
|
||||
[Range(1, 100), Tooltip("Speed of fade in / out: lower is slower")]
|
||||
public byte speed = 1;
|
||||
|
||||
[Tooltip("Reference to Image component on child panel")]
|
||||
public Image fadeImage;
|
||||
|
||||
[Tooltip("Color to use during scene transition")]
|
||||
public Color fadeColor = Color.black;
|
||||
|
||||
WaitForSeconds waitForSeconds;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
waitForSeconds = new WaitForSeconds(speed * 0.01f);
|
||||
}
|
||||
|
||||
public IEnumerator FadeIn()
|
||||
{
|
||||
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeIn - fading image in {fadeImage.color.a}");
|
||||
|
||||
float alpha = fadeImage.color.a;
|
||||
|
||||
while (alpha < 1)
|
||||
{
|
||||
yield return waitForSeconds;
|
||||
alpha += 0.01f;
|
||||
fadeColor.a = alpha;
|
||||
fadeImage.color = fadeColor;
|
||||
}
|
||||
|
||||
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeIn - done fading");
|
||||
}
|
||||
|
||||
public IEnumerator FadeOut()
|
||||
{
|
||||
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeOut - fading image out {fadeImage.color.a}");
|
||||
|
||||
float alpha = fadeImage.color.a;
|
||||
|
||||
while (alpha > 0)
|
||||
{
|
||||
yield return waitForSeconds;
|
||||
alpha -= 0.01f;
|
||||
fadeColor.a = alpha;
|
||||
fadeImage.color = fadeColor;
|
||||
}
|
||||
|
||||
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeOut - done fading");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 363a8867bb9c7b845a73233566df8c1e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
// This script is attached to portal labels to keep them facing the camera
|
||||
public class LookAtMainCamera : MonoBehaviour
|
||||
{
|
||||
// LateUpdate so that all camera updates are finished.
|
||||
void LateUpdate()
|
||||
{
|
||||
transform.forward = Camera.main.transform.forward;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc58300ee45438a418d9e32957fdc0c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
public class PhysicsSimulator : MonoBehaviour
|
||||
{
|
||||
PhysicsScene physicsScene;
|
||||
PhysicsScene2D physicsScene2D;
|
||||
|
||||
bool simulatePhysicsScene;
|
||||
bool simulatePhysicsScene2D;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (NetworkServer.active)
|
||||
{
|
||||
physicsScene = gameObject.scene.GetPhysicsScene();
|
||||
simulatePhysicsScene = physicsScene.IsValid() && physicsScene != Physics.defaultPhysicsScene;
|
||||
|
||||
physicsScene2D = gameObject.scene.GetPhysicsScene2D();
|
||||
simulatePhysicsScene2D = physicsScene2D.IsValid() && physicsScene2D != Physics2D.defaultPhysicsScene;
|
||||
}
|
||||
else
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (!NetworkServer.active) return;
|
||||
|
||||
if (simulatePhysicsScene)
|
||||
physicsScene.Simulate(Time.fixedDeltaTime);
|
||||
|
||||
if (simulatePhysicsScene2D)
|
||||
physicsScene2D.Simulate(Time.fixedDeltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 472efbe031bc80a499ea2a1003ac7452
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
// This sets up the scene camera for the local player
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
public class PlayerCamera : NetworkBehaviour
|
||||
{
|
||||
Camera mainCam;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
mainCam = Camera.main;
|
||||
}
|
||||
|
||||
public override void OnStartLocalPlayer()
|
||||
{
|
||||
if (mainCam != null)
|
||||
{
|
||||
// configure and make camera a child of player with 3rd person offset
|
||||
mainCam.orthographic = false;
|
||||
mainCam.transform.SetParent(transform);
|
||||
mainCam.transform.localPosition = new Vector3(0f, 3f, -8f);
|
||||
mainCam.transform.localEulerAngles = new Vector3(10f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStopLocalPlayer()
|
||||
{
|
||||
if (mainCam != null)
|
||||
{
|
||||
mainCam.transform.SetParent(null);
|
||||
SceneManager.MoveGameObjectToScene(mainCam.gameObject, SceneManager.GetActiveScene());
|
||||
mainCam.orthographic = true;
|
||||
mainCam.transform.localPosition = new Vector3(0f, 70f, 0f);
|
||||
mainCam.transform.localEulerAngles = new Vector3(90f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22976424f775a0f4a8531e6713ff6de2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
[RequireComponent(typeof(CapsuleCollider))]
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
[RequireComponent(typeof(NetworkTransform))]
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class PlayerController : NetworkBehaviour
|
||||
{
|
||||
public CharacterController characterController;
|
||||
|
||||
[Header("Movement Settings")]
|
||||
public float moveSpeed = 8f;
|
||||
public float turnSensitivity = 5f;
|
||||
public float maxTurnSpeed = 100f;
|
||||
|
||||
[Header("Diagnostics")]
|
||||
public float horizontal;
|
||||
public float vertical;
|
||||
public float turn;
|
||||
public float jumpSpeed;
|
||||
public bool isGrounded = true;
|
||||
public bool isFalling;
|
||||
public Vector3 velocity;
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (characterController == null)
|
||||
characterController = GetComponent<CharacterController>();
|
||||
|
||||
characterController.enabled = false;
|
||||
GetComponent<Rigidbody>().isKinematic = true;
|
||||
GetComponent<NetworkTransform>().syncDirection = SyncDirection.ClientToServer;
|
||||
}
|
||||
|
||||
public override void OnStartLocalPlayer()
|
||||
{
|
||||
characterController.enabled = true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!isLocalPlayer || characterController == null || !characterController.enabled)
|
||||
return;
|
||||
|
||||
horizontal = Input.GetAxis("Horizontal");
|
||||
vertical = Input.GetAxis("Vertical");
|
||||
|
||||
// Q and E cancel each other out, reducing the turn to zero
|
||||
if (Input.GetKey(KeyCode.Q))
|
||||
turn = Mathf.MoveTowards(turn, -maxTurnSpeed, turnSensitivity);
|
||||
if (Input.GetKey(KeyCode.E))
|
||||
turn = Mathf.MoveTowards(turn, maxTurnSpeed, turnSensitivity);
|
||||
if (Input.GetKey(KeyCode.Q) && Input.GetKey(KeyCode.E))
|
||||
turn = Mathf.MoveTowards(turn, 0, turnSensitivity);
|
||||
if (!Input.GetKey(KeyCode.Q) && !Input.GetKey(KeyCode.E))
|
||||
turn = Mathf.MoveTowards(turn, 0, turnSensitivity);
|
||||
|
||||
if (isGrounded)
|
||||
isFalling = false;
|
||||
|
||||
if ((isGrounded || !isFalling) && jumpSpeed < 1f && Input.GetKey(KeyCode.Space))
|
||||
{
|
||||
jumpSpeed = Mathf.Lerp(jumpSpeed, 1f, 0.5f);
|
||||
}
|
||||
else if (!isGrounded)
|
||||
{
|
||||
isFalling = true;
|
||||
jumpSpeed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (!isLocalPlayer || characterController == null || !characterController.enabled)
|
||||
return;
|
||||
|
||||
transform.Rotate(0f, turn * Time.fixedDeltaTime, 0f);
|
||||
|
||||
Vector3 direction = new Vector3(horizontal, jumpSpeed, vertical);
|
||||
direction = Vector3.ClampMagnitude(direction, 1f);
|
||||
direction = transform.TransformDirection(direction);
|
||||
direction *= moveSpeed;
|
||||
|
||||
if (jumpSpeed > 0)
|
||||
characterController.Move(direction * Time.fixedDeltaTime);
|
||||
else
|
||||
characterController.SimpleMove(direction);
|
||||
|
||||
isGrounded = characterController.isGrounded;
|
||||
velocity = characterController.velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05e10150710dde14b83d3c8f5aa853c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
91
Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs
Normal file
91
Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
public class Portal : NetworkBehaviour
|
||||
{
|
||||
[Scene, Tooltip("Which scene to send player from here")]
|
||||
public string destinationScene;
|
||||
|
||||
[Tooltip("Where to spawn player in Destination Scene")]
|
||||
public Vector3 startPosition;
|
||||
|
||||
[Tooltip("Reference to child TMP label")]
|
||||
public TextMesh label; // don't depend on TMPro. 2019 errors.
|
||||
|
||||
[SyncVar(hook = nameof(OnLabelTextChanged))]
|
||||
public string labelText;
|
||||
|
||||
public void OnLabelTextChanged(string _, string newValue)
|
||||
{
|
||||
label.text = labelText;
|
||||
}
|
||||
|
||||
// This is approximately the fade time
|
||||
WaitForSeconds waitForFade = new WaitForSeconds(2f);
|
||||
|
||||
public override void OnStartServer()
|
||||
{
|
||||
labelText = Path.GetFileNameWithoutExtension(destinationScene);
|
||||
|
||||
// Simple Regex to insert spaces before capitals, numbers
|
||||
labelText = Regex.Replace(labelText, @"\B[A-Z0-9]+", " $0");
|
||||
}
|
||||
|
||||
// Note that I have created layers called Player(8) and Portal(9) and set them
|
||||
// up in the Physics collision matrix so only Player collides with Portal.
|
||||
void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// tag check in case you didn't set up the layers and matrix as noted above
|
||||
if (!other.CompareTag("Player")) return;
|
||||
|
||||
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} Portal::OnTriggerEnter {gameObject.name} in {gameObject.scene.name}");
|
||||
|
||||
// applies to host client on server and remote clients
|
||||
if (other.TryGetComponent<PlayerController>(out PlayerController playerController))
|
||||
playerController.enabled = false;
|
||||
|
||||
if (isServer)
|
||||
StartCoroutine(SendPlayerToNewScene(other.gameObject));
|
||||
}
|
||||
|
||||
[ServerCallback]
|
||||
IEnumerator SendPlayerToNewScene(GameObject player)
|
||||
{
|
||||
if (player.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
|
||||
{
|
||||
NetworkConnectionToClient conn = identity.connectionToClient;
|
||||
if (conn == null) yield break;
|
||||
|
||||
// Tell client to unload previous subscene. No custom handling for this.
|
||||
conn.Send(new SceneMessage { sceneName = gameObject.scene.path, sceneOperation = SceneOperation.UnloadAdditive, customHandling = true });
|
||||
|
||||
yield return waitForFade;
|
||||
|
||||
//Debug.Log($"SendPlayerToNewScene RemovePlayerForConnection {conn} netId:{conn.identity.netId}");
|
||||
NetworkServer.RemovePlayerForConnection(conn, false);
|
||||
|
||||
// reposition player on server and client
|
||||
player.transform.position = startPosition;
|
||||
player.transform.LookAt(Vector3.up);
|
||||
|
||||
// Move player to new subscene.
|
||||
SceneManager.MoveGameObjectToScene(player, SceneManager.GetSceneByPath(destinationScene));
|
||||
|
||||
// Tell client to load the new subscene with custom handling (see NetworkManager::OnClientChangeScene).
|
||||
conn.Send(new SceneMessage { sceneName = destinationScene, sceneOperation = SceneOperation.LoadAdditive, customHandling = true });
|
||||
|
||||
//Debug.Log($"SendPlayerToNewScene AddPlayerForConnection {conn} netId:{conn.identity.netId}");
|
||||
NetworkServer.AddPlayerForConnection(conn, player);
|
||||
|
||||
// host client would have been disabled by OnTriggerEnter above
|
||||
if (NetworkClient.localPlayer != null && NetworkClient.localPlayer.TryGetComponent<PlayerController>(out PlayerController playerController))
|
||||
playerController.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs.meta
Normal file
11
Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e680878006965146a8f9d85834c4d1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs
Normal file
36
Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.AdditiveLevels
|
||||
{
|
||||
public class RandomColor : NetworkBehaviour
|
||||
{
|
||||
// Color32 packs to 4 bytes
|
||||
[SyncVar(hook = nameof(SetColor))]
|
||||
public Color32 color = Color.black;
|
||||
|
||||
// Unity clones the material when GetComponent<Renderer>().material is called
|
||||
// Cache it here and destroy it in OnDestroy to prevent a memory leak
|
||||
Material cachedMaterial;
|
||||
|
||||
void SetColor(Color32 _, Color32 newColor)
|
||||
{
|
||||
if (cachedMaterial == null) cachedMaterial = GetComponentInChildren<Renderer>().material;
|
||||
cachedMaterial.color = newColor;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
Destroy(cachedMaterial);
|
||||
}
|
||||
|
||||
public override void OnStartServer()
|
||||
{
|
||||
base.OnStartServer();
|
||||
|
||||
// This script is on players that are respawned repeatedly
|
||||
// so once the color has been set, don't change it.
|
||||
if (color == Color.black)
|
||||
color = Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a91a718a70d01b347b75cb768a6f1a92
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user