#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !UPDATE_MANAGER_DISABLE_PROFILER_MARKERS #define ENABLE_PROFILER_MARKERS #endif using System; using LanLib.UpdateManager.Internal; using LanLib.UpdateManager.Jobs; using UnityEngine; namespace LanLib.UpdateManager { /// /// Singleton MonoBehaviour that calls , or to registered objects every frame. /// /// /// Any C# object can be registered for updates, including MonoBehaviours, pure C# classes and structs, as long as they implement , or . /// Managed methods are called inside a try/catch block, so that exceptions don't stop other objects from updating. ///
/// This class doesn't implement any execution order mechanism, so don't rely on managed methods being executed in any order. /// In fact, the order of executed methods will most likely change during the lifetime of the UpdateManager. ///
[ExecuteAlways] public class UpdateManager : MonoBehaviour { /// Get or create the singleton instance public static UpdateManager Instance => (ApplicationUtils.IsQuitting || _instance != null) ? _instance : (_instance = CreateInstance()); protected static UpdateManager _instance; private static UpdateManager CreateInstance() { var gameObject = new GameObject(nameof(UpdateManager)) { hideFlags = HideFlags.DontSave, }; #if UNITY_EDITOR if (!Application.isPlaying) { gameObject.hideFlags = HideFlags.HideAndDontSave; } else #endif { DontDestroyOnLoad(gameObject); } return gameObject.AddComponent(); } #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void DestroyEditorUpdateManager() { if (_instance) { DestroyImmediate(_instance.gameObject); } } #endif /// /// Returns whether there are any objects registered for managed updates. /// public bool HasRegisteredObjects => _updatableObjects.Count > 0 || _lateUpdatableObjects.Count > 0 || _fixedUpdatableObjects.Count > 0; private readonly FastRemoveList _updatableObjects = new FastRemoveList(); private readonly FastRemoveList _lateUpdatableObjects = new FastRemoveList(); private readonly FastRemoveList _fixedUpdatableObjects = new FastRemoveList(); protected void Update() { UpdateJobTime.InstanceRef.Refresh(); foreach (IUpdatable updatable in _updatableObjects) { try { #if ENABLE_PROFILER_MARKERS using (ProfilerMarkerMap.Get("ManagedUpdate", updatable)) #endif updatable.ManagedUpdate(); } catch (Exception ex) { Debug.LogException(ex); } } } protected void LateUpdate() { foreach (ILateUpdatable lateUpdatable in _lateUpdatableObjects) { try { #if ENABLE_PROFILER_MARKERS using (ProfilerMarkerMap.Get("ManagedLateUpdate", lateUpdatable)) #endif lateUpdatable.ManagedLateUpdate(); } catch (Exception ex) { Debug.LogException(ex); } } } protected void FixedUpdate() { foreach (IFixedUpdatable fixedUpdatable in _fixedUpdatableObjects) { try { #if ENABLE_PROFILER_MARKERS using (ProfilerMarkerMap.Get("ManagedFixedUpdate", fixedUpdatable)) #endif fixedUpdatable.ManagedFixedUpdate(); } catch (Exception ex) { Debug.LogException(ex); } } } /// /// Register to be updated every frame. /// /// /// Registering updatable objects is O(1). /// Registering an object more than once is a no-op. /// public void Register(IManagedObject obj) { if (obj is IUpdatable updatable) { _updatableObjects.Add(updatable); } if (obj is ILateUpdatable lateUpdatable) { _lateUpdatableObjects.Add(lateUpdatable); } if (obj is IFixedUpdatable fixedUpdatable) { _fixedUpdatableObjects.Add(fixedUpdatable); } enabled = HasRegisteredObjects; } /// /// Unregister , so it is not updated every frame anymore. /// /// /// Unregistering updatable objects is O(1). /// Unregistering an object that wasn't registered is a no-op. /// public void Unregister(IManagedObject obj) { if (obj is IUpdatable updatable) { _updatableObjects.Remove(updatable); } if (obj is ILateUpdatable lateUpdatable) { _lateUpdatableObjects.Remove(lateUpdatable); } if (obj is IFixedUpdatable fixedUpdatable) { _fixedUpdatableObjects.Remove(fixedUpdatable); } enabled = HasRegisteredObjects; } /// /// Unregisters all updatable objects at once. /// public void Clear() { _updatableObjects.Clear(); _lateUpdatableObjects.Clear(); _fixedUpdatableObjects.Clear(); enabled = false; } } }