// This script is based on the following script by Peter Stirling: https://wiki.unity3d.com/index.php/Floating_Origin using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.Serialization; #if UNITY_EDITOR using UnityEditor; using NWH.NUI; #endif namespace NWH.Common.FloatingOrigin { /// /// Moves scene objects back to origin when the Camera.main is further than distanceThreshold from world origin /// [0,0,0]. /// Works only on current scene. /// public class FloatingOrigin : MonoBehaviour { public static FloatingOrigin Instance; public float distanceThreshold = 500f; public UnityEvent onBeforeJump = new UnityEvent(); public UnityEvent onAfterJump = new UnityEvent(); private Vector3 _totalOffset; private Camera _cameraMain; private Transform _cameraTransform; private Vector3 _cameraPosition; private ParticleSystem.Particle[] _particles; public Vector3 TotalOffset { get { return _totalOffset; } } private void Awake() { Debug.Assert(Instance == null, "Only one FloatingOrigin script can be present in a scene."); Instance = this; onBeforeJump.AddListener(BeforeJump); onAfterJump.AddListener(AfterJump); } private List FindObjects() where T : UnityEngine.Object { return FindObjectsOfType().ToList(); // Not the fastest solution } private void BeforeJump() { foreach (Rigidbody rb in FindObjects()) { rb.sleepThreshold = float.MaxValue; } } private void AfterJump() { foreach (Rigidbody rb in FindObjects()) { rb.sleepThreshold = 0.14f; } Physics.SyncTransforms(); } private void LateUpdate() { _cameraMain = Camera.main; if (_cameraMain == null) { return; } _cameraTransform = _cameraMain.transform; _cameraPosition = _cameraTransform.position; if (_cameraPosition.magnitude > distanceThreshold) { Jump(); } } private void Jump() { onBeforeJump.Invoke(); _totalOffset += _cameraPosition; // Move root transforms for (int i = 0; i < SceneManager.sceneCount; i++) { foreach (GameObject g in SceneManager.GetSceneAt(i).GetRootGameObjects()) { g.transform.position -= _cameraPosition; } } // Move particles foreach (ParticleSystem ps in FindObjects()) { ParticleSystem.MainModule main = ps.main; if (main.simulationSpace != ParticleSystemSimulationSpace.World) { continue; } int maxParticles = main.maxParticles; if (maxParticles == 0) { continue; } bool wasPaused = ps.isPaused; bool wasPlaying = ps.isPlaying; if (!wasPaused) { ps.Pause(); } if (_particles == null || _particles.Length < maxParticles) { _particles = new ParticleSystem.Particle[maxParticles]; } int num = ps.GetParticles(_particles); for (int i = 0; i < num; i++) { _particles[i].position -= _cameraPosition; } ps.SetParticles(_particles, num); if (wasPlaying) { ps.Play(); } } onAfterJump.Invoke(); } } } #if UNITY_EDITOR namespace NWH.Common.FloatingOrigin { [CustomEditor(typeof(FloatingOrigin))] public class FloatingOriginEditor : NUIEditor { public override bool OnInspectorNUI() { if (!base.OnInspectorNUI()) { return false; } drawer.Field("distanceThreshold"); drawer.Field("OnBeforeJump"); drawer.Field("OnAfterJump"); drawer.EndEditor(this); return true; } } } #endif