using FishNet.Connection; using FishNet.Managing.Logging; using FishNet.Managing.Scened; using FishNet.Object; using System.Collections.Generic; using UnityEngine; namespace FishNet.Example.Scened { /// /// Loads a single scene, additive scenes, or both when a client /// enters or exits this trigger. /// public class SceneLoaderExample : MonoBehaviour { /// /// True to move the triggering object. /// [Tooltip("True to move the triggering object.")] [SerializeField] private bool _moveObject = true; /// /// True to move all connection objects (clients). /// [Tooltip("True to move all connection objects (clients).")] [SerializeField] private bool _moveAllObjects; /// /// True to replace current scenes with new scenes. First scene loaded will become active scene. /// [Tooltip("True to replace current scenes with new scenes. First scene loaded will become active scene.")] [SerializeField] private ReplaceOption _replaceOption = ReplaceOption.None; /// /// Scenes to load. /// [Tooltip("Scenes to load.")] [SerializeField] private string[] _scenes = new string[0]; /// /// True to only unload for the connectioning causing the trigger. /// [Tooltip("True to only unload for the connectioning causing the trigger.")] [SerializeField] private bool _connectionOnly; /// /// True to automatically unload the loaded scenes when no more connections are using them. /// [Tooltip("True to automatically unload the loaded scenes when no more connections are using them.")] [SerializeField] private bool _automaticallyUnload = true; /// /// True to fire when entering the trigger. False to fire when exiting the trigger. /// [Tooltip("True to fire when entering the trigger. False to fire when exiting the trigger.")] [SerializeField] private bool _onTriggerEnter = true; /// /// Used to prevent excessive triggering when two clients are loaded and server is separate. /// Client may enter trigger intentionally then when moved to a new scene will re-enter trigger /// since original scene will still be loaded on server due to another client being in it. /// This scenario is extremely unlikely in production but keep it in mind. /// private Dictionary _triggeredTimes = new Dictionary(); [Server(Logging = LoggingType.Off)] private void OnTriggerEnter(Collider other) { if (!_onTriggerEnter) return; LoadScene(other.GetComponent()); } [Server(Logging = LoggingType.Off)] private void OnTriggerExit(Collider other) { if (_onTriggerEnter) return; LoadScene(other.GetComponent()); } private void LoadScene(NetworkObject triggeringIdentity) { if (!InstanceFinder.NetworkManager.IsServer) return; //NetworkObject isn't necessarily needed but to ensure its the player only run if found. if (triggeringIdentity == null) return; /* Dont let trigger hit twice by same connection too frequently * See _triggeredTimes field for more info. */ if (_triggeredTimes.TryGetValue(triggeringIdentity.Owner, out float time)) { if (Time.time - time < 0.5f) return; } _triggeredTimes[triggeringIdentity.Owner] = Time.time; //Which objects to move. List movedObjects = new List(); if (_moveAllObjects) { foreach (NetworkConnection item in InstanceFinder.ServerManager.Clients.Values) { foreach (NetworkObject nob in item.Objects) movedObjects.Add(nob); } } else if (_moveObject) { movedObjects.Add(triggeringIdentity); } //Load options. LoadOptions loadOptions = new LoadOptions { AutomaticallyUnload = _automaticallyUnload, }; //Make scene data. SceneLoadData sld = new SceneLoadData(_scenes); sld.PreferredActiveScene = sld.SceneLookupDatas[0]; sld.ReplaceScenes = _replaceOption; sld.Options = loadOptions; sld.MovedNetworkObjects = movedObjects.ToArray(); //Load for connection only. if (_connectionOnly) InstanceFinder.SceneManager.LoadConnectionScenes(triggeringIdentity.Owner, sld); //Load for all clients. else InstanceFinder.SceneManager.LoadGlobalScenes(sld); } } }