using UnityEngine; using System.Collections.Generic; using UnityEngine.Assertions; #if UNITY_EDITOR using UnityEditor; #endif #if UNITY_5_5_OR_NEWER using UnityEngine.Profiling; #endif // ///////////////////////////////////////////////////////////////////////////////////////// // More Effective Coroutines Pro // v3.08.0 // // This is an improved implementation of coroutines that boasts zero per-frame memory allocations, // runs about twice as fast as Unity's built in coroutines, and has a range of extra features. // // For manual, support, or upgrade guide visit http://trinary.tech/ // // Created by Teal Rogers // Trinary Software // All rights preserved // trinaryllc@gmail.com // ///////////////////////////////////////////////////////////////////////////////////////// namespace MEC { public class Timing : MonoBehaviour { /// /// The time between calls to SlowUpdate. /// [Tooltip("How quickly the SlowUpdate segment ticks.")] public float TimeBetweenSlowUpdateCalls = 1f / 7f; /// /// The amount that each coroutine should be seperated inside the Unity profiler. NOTE: When the profiler window /// is not open this value is ignored and all coroutines behave as if "None" is selected. /// [Tooltip("How much data should be sent to the profiler window when it's open.")] public DebugInfoType ProfilerDebugAmount; /// /// Whether the manual timeframe should automatically trigger during the update segment. /// [Tooltip("When using manual timeframe, should it run automatically after the update loop or only when TriggerManualTimframeUpdate is called.")] public bool AutoTriggerManualTimeframe = true; /// /// The number of coroutines that are being run in the Update segment. /// [Tooltip("A count of the number of Update coroutines that are currently running."), Space(12)] public int UpdateCoroutines; /// /// The number of coroutines that are being run in the FixedUpdate segment. /// [Tooltip("A count of the number of FixedUpdate coroutines that are currently running.")] public int FixedUpdateCoroutines; /// /// The number of coroutines that are being run in the LateUpdate segment. /// [Tooltip("A count of the number of LateUpdate coroutines that are currently running.")] public int LateUpdateCoroutines; /// /// The number of coroutines that are being run in the SlowUpdate segment. /// [Tooltip("A count of the number of SlowUpdate coroutines that are currently running.")] public int SlowUpdateCoroutines; /// /// The number of coroutines that are being run in the RealtimeUpdate segment. /// [Tooltip("A count of the number of RealtimeUpdate coroutines that are currently running.")] public int RealtimeUpdateCoroutines; /// /// The number of coroutines that are being run in the EditorUpdate segment. /// [Tooltip("A count of the number of EditorUpdate coroutines that are currently running.")] public int EditorUpdateCoroutines; /// /// The number of coroutines that are being run in the EditorSlowUpdate segment. /// [Tooltip("A count of the number of EditorSlowUpdate coroutines that are currently running.")] public int EditorSlowUpdateCoroutines; /// /// The number of coroutines that are being run in the EndOfFrame segment. /// [Tooltip("A count of the number of EndOfFrame coroutines that are currently running.")] public int EndOfFrameCoroutines; /// /// The number of coroutines that are being run in the ManualTimeframe segment. /// [Tooltip("A count of the number of ManualTimeframe coroutines that are currently running.")] public int ManualTimeframeCoroutines; /// /// The time in seconds that the current segment has been running. /// [System.NonSerialized] public float localTime; /// /// The time in seconds that the current segment has been running. /// public static float LocalTime { get { return Instance.localTime; } } /// /// The amount of time in fractional seconds that elapsed between this frame and the last frame. /// [System.NonSerialized] public float deltaTime; /// /// The amount of time in fractional seconds that elapsed between this frame and the last frame. /// public static float DeltaTime { get { return Instance.deltaTime; } } /// /// When defined, this function will be called every time manual timeframe needs to be set. The last manual timeframe time is passed in, and /// the new manual timeframe time needs to be returned. If this function is left as null, manual timeframe will be set to the current Time.time. /// public System.Func SetManualTimeframeTime; /// /// Used for advanced coroutine control. /// public static System.Func, CoroutineHandle, IEnumerator> ReplacementFunction; /// /// This event fires just before each segment is run. /// public static event System.Action OnPreExecute; /// /// You can use "yield return Timing.WaitForOneFrame;" inside a coroutine function to go to the next frame. /// public const float WaitForOneFrame = float.NegativeInfinity; /// /// The main thread that (almost) everything in unity runs in. /// public static System.Threading.Thread MainThread { get; private set; } /// /// The handle of the current coroutine that is running. /// public static CoroutineHandle CurrentCoroutine { get { return Instance.currentCoroutine; } } /// /// The handle of the current coroutine that is running. /// public CoroutineHandle currentCoroutine { get; private set; } private static object _tmpRef; private static int _tmpInt; private static bool _tmpBool; private static Segment _tmpSegment; private static CoroutineHandle _tmpHandle; private int _currentUpdateFrame; private int _currentLateUpdateFrame; private int _currentSlowUpdateFrame; private int _currentRealtimeUpdateFrame; private int _currentEndOfFrameFrame; private int _nextUpdateProcessSlot; private int _nextLateUpdateProcessSlot; private int _nextFixedUpdateProcessSlot; private int _nextSlowUpdateProcessSlot; private int _nextRealtimeUpdateProcessSlot; private int _nextEditorUpdateProcessSlot; private int _nextEditorSlowUpdateProcessSlot; private int _nextEndOfFrameProcessSlot; private int _nextManualTimeframeProcessSlot; private int _lastUpdateProcessSlot; private int _lastLateUpdateProcessSlot; private int _lastFixedUpdateProcessSlot; private int _lastSlowUpdateProcessSlot; private int _lastRealtimeUpdateProcessSlot; #if UNITY_EDITOR private int _lastEditorUpdateProcessSlot; private int _lastEditorSlowUpdateProcessSlot; #endif private int _lastEndOfFrameProcessSlot; private int _lastManualTimeframeProcessSlot; private float _lastUpdateTime; private float _lastLateUpdateTime; private float _lastFixedUpdateTime; private float _lastSlowUpdateTime; private float _lastRealtimeUpdateTime; #if UNITY_EDITOR private float _lastEditorUpdateTime; private float _lastEditorSlowUpdateTime; #endif private float _lastEndOfFrameTime; private float _lastManualTimeframeTime; private float _lastSlowUpdateDeltaTime; private float _lastEditorUpdateDeltaTime; private float _lastEditorSlowUpdateDeltaTime; private float _lastManualTimeframeDeltaTime; private ushort _framesSinceUpdate; private ushort _expansions = 1; [SerializeField, HideInInspector] private byte _instanceID; private bool _EOFPumpRan; private static readonly Dictionary> Links = new Dictionary>(); private static readonly WaitForEndOfFrame EofWaitObject = new WaitForEndOfFrame(); private readonly Dictionary> _waitingTriggers = new Dictionary>(); private readonly HashSet _allWaiting = new HashSet(); private readonly Dictionary _handleToIndex = new Dictionary(); private readonly Dictionary _indexToHandle = new Dictionary(); private readonly Dictionary _processTags = new Dictionary(); private readonly Dictionary> _taggedProcesses = new Dictionary>(); private readonly Dictionary _processLayers = new Dictionary(); private readonly Dictionary> _layeredProcesses = new Dictionary>(); private IEnumerator[] UpdateProcesses = new IEnumerator[InitialBufferSizeLarge]; private IEnumerator[] LateUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; private IEnumerator[] FixedUpdateProcesses = new IEnumerator[InitialBufferSizeMedium]; private IEnumerator[] SlowUpdateProcesses = new IEnumerator[InitialBufferSizeMedium]; private IEnumerator[] RealtimeUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; private IEnumerator[] EditorUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; private IEnumerator[] EditorSlowUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; private IEnumerator[] EndOfFrameProcesses = new IEnumerator[InitialBufferSizeSmall]; private IEnumerator[] ManualTimeframeProcesses = new IEnumerator[InitialBufferSizeSmall]; private bool[] UpdatePaused = new bool[InitialBufferSizeLarge]; private bool[] LateUpdatePaused = new bool[InitialBufferSizeSmall]; private bool[] FixedUpdatePaused = new bool[InitialBufferSizeMedium]; private bool[] SlowUpdatePaused = new bool[InitialBufferSizeMedium]; private bool[] RealtimeUpdatePaused = new bool[InitialBufferSizeSmall]; private bool[] EditorUpdatePaused = new bool[InitialBufferSizeSmall]; private bool[] EditorSlowUpdatePaused = new bool[InitialBufferSizeSmall]; private bool[] EndOfFramePaused = new bool[InitialBufferSizeSmall]; private bool[] ManualTimeframePaused = new bool[InitialBufferSizeSmall]; private bool[] UpdateHeld = new bool[InitialBufferSizeLarge]; private bool[] LateUpdateHeld = new bool[InitialBufferSizeSmall]; private bool[] FixedUpdateHeld = new bool[InitialBufferSizeMedium]; private bool[] SlowUpdateHeld = new bool[InitialBufferSizeMedium]; private bool[] RealtimeUpdateHeld = new bool[InitialBufferSizeSmall]; private bool[] EditorUpdateHeld = new bool[InitialBufferSizeSmall]; private bool[] EditorSlowUpdateHeld = new bool[InitialBufferSizeSmall]; private bool[] EndOfFrameHeld = new bool[InitialBufferSizeSmall]; private bool[] ManualTimeframeHeld = new bool[InitialBufferSizeSmall]; private CoroutineHandle _eofWatcherHandle; private const ushort FramesUntilMaintenance = 64; private const int ProcessArrayChunkSize = 64; private const int InitialBufferSizeLarge = 256; private const int InitialBufferSizeMedium = 64; private const int InitialBufferSizeSmall = 8; private static Timing[] ActiveInstances = new Timing[16]; private static Timing _instance; public static Timing Instance { get { if (_instance == null || !_instance.gameObject) { GameObject instanceHome = GameObject.Find("Timing Controller"); if(instanceHome == null) { instanceHome = new GameObject { name = "Timing Controller" }; #if UNITY_EDITOR if(Application.isPlaying) DontDestroyOnLoad(instanceHome); #else DontDestroyOnLoad(instanceHome); #endif } _instance = instanceHome.GetComponent() ?? instanceHome.AddComponent(); _instance.InitializeInstanceID(); } return _instance; } set { _instance = value; } } void OnDestroy() { if (_instance == this) _instance = null; } void OnEnable() { if (MainThread == null) MainThread = System.Threading.Thread.CurrentThread; if (_nextEditorUpdateProcessSlot > 0 || _nextEditorSlowUpdateProcessSlot > 0) OnEditorStart(); InitializeInstanceID(); if (_nextEndOfFrameProcessSlot > 0) RunCoroutineSingletonOnInstance(_EOFPumpWatcher(), "MEC_EOFPumpWatcher", SingletonBehavior.Abort); } void OnDisable() { if (_instanceID < ActiveInstances.Length) ActiveInstances[_instanceID] = null; } private void InitializeInstanceID() { if (ActiveInstances[_instanceID] == null) { if (_instanceID == 0x00) _instanceID++; for (; _instanceID <= 0x10; _instanceID++) { if (_instanceID == 0x10) { GameObject.Destroy(gameObject); throw new System.OverflowException("You are only allowed 15 different contexts for MEC to run inside at one time."); } if (ActiveInstances[_instanceID] == null) { ActiveInstances[_instanceID] = this; break; } } } } void Update() { if (OnPreExecute != null) OnPreExecute(); if (_lastSlowUpdateTime + TimeBetweenSlowUpdateCalls < Time.realtimeSinceStartup && _nextSlowUpdateProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.SlowUpdate }; if (UpdateTimeValues(coindex.seg)) _lastSlowUpdateProcessSlot = _nextSlowUpdateProcessSlot; for (coindex.i = 0; coindex.i < _lastSlowUpdateProcessSlot; coindex.i++) { try { if (!SlowUpdatePaused[coindex.i] && !SlowUpdateHeld[coindex.i] && SlowUpdateProcesses[coindex.i] != null && !(localTime < SlowUpdateProcesses[coindex.i].Current)) { currentCoroutine = _indexToHandle[coindex]; if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) { Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine (Slow Update), " + (_processLayers.ContainsKey(_indexToHandle[coindex]) ? "layer " + _processLayers[_indexToHandle[coindex]] : "no layer") + (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) : "Processing Coroutine (Slow Update)"); } if (!SlowUpdateProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (SlowUpdateProcesses[coindex.i] != null && float.IsNaN(SlowUpdateProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { SlowUpdateProcesses[coindex.i] = ReplacementFunction(SlowUpdateProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } catch(System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.SlowUpdate);"); } } } if (_nextRealtimeUpdateProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.RealtimeUpdate }; if (UpdateTimeValues(coindex.seg)) _lastRealtimeUpdateProcessSlot = _nextRealtimeUpdateProcessSlot; for (coindex.i = 0; coindex.i < _lastRealtimeUpdateProcessSlot; coindex.i++) { try { if (!RealtimeUpdatePaused[coindex.i] && !RealtimeUpdateHeld[coindex.i] && RealtimeUpdateProcesses[coindex.i] != null && !(localTime < RealtimeUpdateProcesses[coindex.i].Current)) { currentCoroutine = _indexToHandle[coindex]; if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) { Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine (Realtime Update), " + (_processLayers.ContainsKey(_indexToHandle[coindex]) ? "layer " + _processLayers[_indexToHandle[coindex]] : "no layer") + (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) : "Processing Coroutine (Realtime Update)"); } if (!RealtimeUpdateProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (RealtimeUpdateProcesses[coindex.i] != null && float.IsNaN(RealtimeUpdateProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { RealtimeUpdateProcesses[coindex.i] = ReplacementFunction(RealtimeUpdateProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.RealtimeUpdate);"); } } } if (_nextUpdateProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.Update }; if (UpdateTimeValues(coindex.seg)) _lastUpdateProcessSlot = _nextUpdateProcessSlot; for (coindex.i = 0; coindex.i < _lastUpdateProcessSlot; coindex.i++) { try { if (!UpdatePaused[coindex.i] && !UpdateHeld[coindex.i] && UpdateProcesses[coindex.i] != null && !(localTime < UpdateProcesses[coindex.i].Current)) { currentCoroutine = _indexToHandle[coindex]; if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) { Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + (_processLayers.ContainsKey(_indexToHandle[coindex]) ? "layer " + _processLayers[_indexToHandle[coindex]] : "no layer") + (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) : "Processing Coroutine"); } if (!UpdateProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (UpdateProcesses[coindex.i] != null && float.IsNaN(UpdateProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { UpdateProcesses[coindex.i] = ReplacementFunction(UpdateProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.Update);"); } } } if (AutoTriggerManualTimeframe) { TriggerManualTimeframeUpdate(); } else { if (++_framesSinceUpdate > FramesUntilMaintenance) { _framesSinceUpdate = 0; if (ProfilerDebugAmount != DebugInfoType.None) Profiler.BeginSample("Maintenance Task"); RemoveUnused(); if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } currentCoroutine = default(CoroutineHandle); } void FixedUpdate() { if (OnPreExecute != null) OnPreExecute(); if (_nextFixedUpdateProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.FixedUpdate }; if (UpdateTimeValues(coindex.seg)) _lastFixedUpdateProcessSlot = _nextFixedUpdateProcessSlot; for (coindex.i = 0; coindex.i < _lastFixedUpdateProcessSlot; coindex.i++) { try { if (!FixedUpdatePaused[coindex.i] && !FixedUpdateHeld[coindex.i] && FixedUpdateProcesses[coindex.i] != null && !(localTime < FixedUpdateProcesses[coindex.i].Current)) { currentCoroutine = _indexToHandle[coindex]; if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) { Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + (_processLayers.ContainsKey(_indexToHandle[coindex]) ? "layer " + _processLayers[_indexToHandle[coindex]] : "no layer") + (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) : "Processing Coroutine"); } if (!FixedUpdateProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (FixedUpdateProcesses[coindex.i] != null && float.IsNaN(FixedUpdateProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { FixedUpdateProcesses[coindex.i] = ReplacementFunction(FixedUpdateProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.FixedUpdate);"); } } currentCoroutine = default(CoroutineHandle); } } void LateUpdate() { if (OnPreExecute != null) OnPreExecute(); if (_nextLateUpdateProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.LateUpdate }; if (UpdateTimeValues(coindex.seg)) _lastLateUpdateProcessSlot = _nextLateUpdateProcessSlot; for (coindex.i = 0; coindex.i < _lastLateUpdateProcessSlot; coindex.i++) { try { if (!LateUpdatePaused[coindex.i] && !LateUpdateHeld[coindex.i] && LateUpdateProcesses[coindex.i] != null && !(localTime < LateUpdateProcesses[coindex.i].Current)) { currentCoroutine = _indexToHandle[coindex]; if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) { Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + (_processLayers.ContainsKey(_indexToHandle[coindex]) ? "layer " + _processLayers[_indexToHandle[coindex]] : "no layer") + (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) : "Processing Coroutine"); } if (!LateUpdateProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (LateUpdateProcesses[coindex.i] != null && float.IsNaN(LateUpdateProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { LateUpdateProcesses[coindex.i] = ReplacementFunction(LateUpdateProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.LateUpdate);"); } } currentCoroutine = default(CoroutineHandle); } } /// /// This will trigger an update in the manual timeframe segment. If the AutoTriggerManualTimeframeDuringUpdate variable is set to true /// then this function will be automitically called every Update, so you would normally want to set that variable to false before /// calling this function yourself. /// public void TriggerManualTimeframeUpdate() { if (OnPreExecute != null) OnPreExecute(); if (_nextManualTimeframeProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.ManualTimeframe }; if (UpdateTimeValues(coindex.seg)) _lastManualTimeframeProcessSlot = _nextManualTimeframeProcessSlot; for (coindex.i = 0; coindex.i < _lastManualTimeframeProcessSlot; coindex.i++) { try { if (!ManualTimeframePaused[coindex.i] && !ManualTimeframeHeld[coindex.i] && ManualTimeframeProcesses[coindex.i] != null && !(localTime < ManualTimeframeProcesses[coindex.i].Current)) { currentCoroutine = _indexToHandle[coindex]; if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) { Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine (Manual Timeframe), " + (_processLayers.ContainsKey(_indexToHandle[coindex]) ? "layer " + _processLayers[_indexToHandle[coindex]] : "no layer") + (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) : "Processing Coroutine (Manual Timeframe)"); } if (!ManualTimeframeProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (ManualTimeframeProcesses[coindex.i] != null && float.IsNaN(ManualTimeframeProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { ManualTimeframeProcesses[coindex.i] = ReplacementFunction(ManualTimeframeProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.ManualTimeframe);"); } } } if (++_framesSinceUpdate > FramesUntilMaintenance) { _framesSinceUpdate = 0; if (ProfilerDebugAmount != DebugInfoType.None) Profiler.BeginSample("Maintenance Task"); RemoveUnused(); if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } currentCoroutine = default(CoroutineHandle); } private bool OnEditorStart() { #if UNITY_EDITOR if(EditorApplication.isPlayingOrWillChangePlaymode) return false; if (_lastEditorUpdateTime < 0.001) _lastEditorUpdateTime = (float)EditorApplication.timeSinceStartup; if (ActiveInstances[_instanceID] == null) OnEnable(); EditorApplication.update -= OnEditorUpdate; EditorApplication.update += OnEditorUpdate; return true; #else return false; #endif } #if UNITY_EDITOR private void OnEditorUpdate() { if (OnPreExecute != null) OnPreExecute(); if (EditorApplication.isPlayingOrWillChangePlaymode) { for(int i = 0;i < _nextEditorUpdateProcessSlot;i++) EditorUpdateProcesses[i] = null; _nextEditorUpdateProcessSlot = 0; for (int i = 0; i < _nextEditorSlowUpdateProcessSlot; i++) EditorSlowUpdateProcesses[i] = null; _nextEditorSlowUpdateProcessSlot = 0; EditorApplication.update -= OnEditorUpdate; _instance = null; } if (_lastEditorSlowUpdateTime + TimeBetweenSlowUpdateCalls < EditorApplication.timeSinceStartup && _nextEditorSlowUpdateProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.EditorSlowUpdate }; if (UpdateTimeValues(coindex.seg)) _lastEditorSlowUpdateProcessSlot = _nextEditorSlowUpdateProcessSlot; for (coindex.i = 0; coindex.i < _lastEditorSlowUpdateProcessSlot; coindex.i++) { currentCoroutine = _indexToHandle[coindex]; try { if (!EditorSlowUpdatePaused[coindex.i] && !EditorSlowUpdateHeld[coindex.i] && EditorSlowUpdateProcesses[coindex.i] != null && !(EditorApplication.timeSinceStartup < EditorSlowUpdateProcesses[coindex.i].Current)) { if (!EditorSlowUpdateProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (EditorSlowUpdateProcesses[coindex.i] != null && float.IsNaN(EditorSlowUpdateProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { EditorSlowUpdateProcesses[coindex.i] = ReplacementFunction(EditorSlowUpdateProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.EditorUpdate);"); } } } if(_nextEditorUpdateProcessSlot > 0) { ProcessIndex coindex = new ProcessIndex { seg = Segment.EditorUpdate }; if (UpdateTimeValues(coindex.seg)) _lastEditorUpdateProcessSlot = _nextEditorUpdateProcessSlot; for (coindex.i = 0; coindex.i < _lastEditorUpdateProcessSlot; coindex.i++) { currentCoroutine = _indexToHandle[coindex]; try { if (!EditorUpdatePaused[coindex.i] && !EditorUpdateHeld[coindex.i] && EditorUpdateProcesses[coindex.i] != null && !(EditorApplication.timeSinceStartup < EditorUpdateProcesses[coindex.i].Current)) { if (!EditorUpdateProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (EditorUpdateProcesses[coindex.i] != null && float.IsNaN(EditorUpdateProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { EditorUpdateProcesses[coindex.i] = ReplacementFunction(EditorUpdateProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.EditorUpdate);"); } } } if (++_framesSinceUpdate > FramesUntilMaintenance) { _framesSinceUpdate = 0; EditorRemoveUnused(); } currentCoroutine = default(CoroutineHandle); } #endif private IEnumerator _EOFPumpWatcher() { while (_nextEndOfFrameProcessSlot > 0) { if(!_EOFPumpRan) base.StartCoroutine(_EOFPump()); _EOFPumpRan = false; yield return WaitForOneFrame; } _EOFPumpRan = false; } private System.Collections.IEnumerator _EOFPump() { while(_nextEndOfFrameProcessSlot > 0) { yield return EofWaitObject; if (OnPreExecute != null) OnPreExecute(); ProcessIndex coindex = new ProcessIndex { seg = Segment.EndOfFrame }; _EOFPumpRan = true; if (UpdateTimeValues(coindex.seg)) _lastEndOfFrameProcessSlot = _nextEndOfFrameProcessSlot; for(coindex.i = 0;coindex.i < _lastEndOfFrameProcessSlot;coindex.i++) { try { if (!EndOfFramePaused[coindex.i] && !EndOfFrameHeld[coindex.i] && EndOfFrameProcesses[coindex.i] != null && !(localTime < EndOfFrameProcesses[coindex.i].Current)) { currentCoroutine = _indexToHandle[coindex]; if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) { Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + (_processLayers.ContainsKey(_indexToHandle[coindex]) ? "layer " + _processLayers[_indexToHandle[coindex]] : "no layer") + (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) : "Processing Coroutine"); } if (!EndOfFrameProcesses[coindex.i].MoveNext()) { if (_indexToHandle.ContainsKey(coindex)) KillCoroutinesOnInstance(_indexToHandle[coindex]); } else if (EndOfFrameProcesses[coindex.i] != null && float.IsNaN(EndOfFrameProcesses[coindex.i].Current)) { if (ReplacementFunction != null) { EndOfFrameProcesses[coindex.i] = ReplacementFunction(EndOfFrameProcesses[coindex.i], _indexToHandle[coindex]); ReplacementFunction = null; } coindex.i--; } if (ProfilerDebugAmount != DebugInfoType.None) Profiler.EndSample(); } } catch (System.Exception ex) { Debug.LogException(ex); if (ex is MissingReferenceException) Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.EndOfFrame);"); } } } currentCoroutine = default(CoroutineHandle); } private void RemoveUnused() { var waitTrigsEnum = _waitingTriggers.GetEnumerator(); while (waitTrigsEnum.MoveNext()) { if (waitTrigsEnum.Current.Value.Count == 0) { _waitingTriggers.Remove(waitTrigsEnum.Current.Key); waitTrigsEnum = _waitingTriggers.GetEnumerator(); continue; } if (_handleToIndex.ContainsKey(waitTrigsEnum.Current.Key) && CoindexIsNull(_handleToIndex[waitTrigsEnum.Current.Key])) { CloseWaitingProcess(waitTrigsEnum.Current.Key); waitTrigsEnum = _waitingTriggers.GetEnumerator(); } } ProcessIndex outer, inner; outer.seg = inner.seg = Segment.Update; for (outer.i = inner.i = 0; outer.i < _nextUpdateProcessSlot; outer.i++) { if (UpdateProcesses[outer.i] != null) { if (outer.i != inner.i) { UpdateProcesses[inner.i] = UpdateProcesses[outer.i]; UpdatePaused[inner.i] = UpdatePaused[outer.i]; UpdateHeld[inner.i] = UpdateHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextUpdateProcessSlot; outer.i++) { UpdateProcesses[outer.i] = null; UpdatePaused[outer.i] = false; UpdateHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } UpdateCoroutines = _nextUpdateProcessSlot = inner.i; outer.seg = inner.seg = Segment.FixedUpdate; for (outer.i = inner.i = 0; outer.i < _nextFixedUpdateProcessSlot; outer.i++) { if (FixedUpdateProcesses[outer.i] != null) { if (outer.i != inner.i) { FixedUpdateProcesses[inner.i] = FixedUpdateProcesses[outer.i]; FixedUpdatePaused[inner.i] = FixedUpdatePaused[outer.i]; FixedUpdateHeld[inner.i] = FixedUpdateHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextFixedUpdateProcessSlot; outer.i++) { FixedUpdateProcesses[outer.i] = null; FixedUpdatePaused[outer.i] = false; FixedUpdateHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } FixedUpdateCoroutines = _nextFixedUpdateProcessSlot = inner.i; outer.seg = inner.seg = Segment.LateUpdate; for (outer.i = inner.i = 0; outer.i < _nextLateUpdateProcessSlot; outer.i++) { if (LateUpdateProcesses[outer.i] != null) { if (outer.i != inner.i) { LateUpdateProcesses[inner.i] = LateUpdateProcesses[outer.i]; LateUpdatePaused[inner.i] = LateUpdatePaused[outer.i]; LateUpdateHeld[inner.i] = LateUpdateHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextLateUpdateProcessSlot; outer.i++) { LateUpdateProcesses[outer.i] = null; LateUpdatePaused[outer.i] = false; LateUpdateHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } LateUpdateCoroutines = _nextLateUpdateProcessSlot = inner.i; outer.seg = inner.seg = Segment.SlowUpdate; for (outer.i = inner.i = 0; outer.i < _nextSlowUpdateProcessSlot; outer.i++) { if (SlowUpdateProcesses[outer.i] != null) { if (outer.i != inner.i) { SlowUpdateProcesses[inner.i] = SlowUpdateProcesses[outer.i]; SlowUpdatePaused[inner.i] = SlowUpdatePaused[outer.i]; SlowUpdateHeld[inner.i] = SlowUpdateHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextSlowUpdateProcessSlot; outer.i++) { SlowUpdateProcesses[outer.i] = null; SlowUpdatePaused[outer.i] = false; SlowUpdateHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } SlowUpdateCoroutines = _nextSlowUpdateProcessSlot = inner.i; outer.seg = inner.seg = Segment.RealtimeUpdate; for (outer.i = inner.i = 0; outer.i < _nextRealtimeUpdateProcessSlot; outer.i++) { if (RealtimeUpdateProcesses[outer.i] != null) { if (outer.i != inner.i) { RealtimeUpdateProcesses[inner.i] = RealtimeUpdateProcesses[outer.i]; RealtimeUpdatePaused[inner.i] = RealtimeUpdatePaused[outer.i]; RealtimeUpdateHeld[inner.i] = RealtimeUpdateHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextRealtimeUpdateProcessSlot; outer.i++) { RealtimeUpdateProcesses[outer.i] = null; RealtimeUpdatePaused[outer.i] = false; RealtimeUpdateHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } RealtimeUpdateCoroutines = _nextRealtimeUpdateProcessSlot = inner.i; outer.seg = inner.seg = Segment.EndOfFrame; for (outer.i = inner.i = 0; outer.i < _nextEndOfFrameProcessSlot; outer.i++) { if (EndOfFrameProcesses[outer.i] != null) { if (outer.i != inner.i) { EndOfFrameProcesses[inner.i] = EndOfFrameProcesses[outer.i]; EndOfFramePaused[inner.i] = EndOfFramePaused[outer.i]; EndOfFrameHeld[inner.i] = EndOfFrameHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextEndOfFrameProcessSlot; outer.i++) { EndOfFrameProcesses[outer.i] = null; EndOfFramePaused[outer.i] = false; EndOfFrameHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } EndOfFrameCoroutines = _nextEndOfFrameProcessSlot = inner.i; outer.seg = inner.seg = Segment.ManualTimeframe; for (outer.i = inner.i = 0; outer.i < _nextManualTimeframeProcessSlot; outer.i++) { if (ManualTimeframeProcesses[outer.i] != null) { if (outer.i != inner.i) { ManualTimeframeProcesses[inner.i] = ManualTimeframeProcesses[outer.i]; ManualTimeframePaused[inner.i] = ManualTimeframePaused[outer.i]; ManualTimeframeHeld[inner.i] = ManualTimeframeHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextManualTimeframeProcessSlot; outer.i++) { ManualTimeframeProcesses[outer.i] = null; ManualTimeframePaused[outer.i] = false; ManualTimeframeHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } ManualTimeframeCoroutines = _nextManualTimeframeProcessSlot = inner.i; } private void EditorRemoveUnused() { var waitTrigsEnum = _waitingTriggers.GetEnumerator(); while (waitTrigsEnum.MoveNext()) { if (_handleToIndex.ContainsKey(waitTrigsEnum.Current.Key) && CoindexIsNull(_handleToIndex[waitTrigsEnum.Current.Key])) { CloseWaitingProcess(waitTrigsEnum.Current.Key); waitTrigsEnum = _waitingTriggers.GetEnumerator(); } } ProcessIndex outer, inner; outer.seg = inner.seg = Segment.EditorUpdate; for (outer.i = inner.i = 0; outer.i < _nextEditorUpdateProcessSlot; outer.i++) { if (EditorUpdateProcesses[outer.i] != null) { if (outer.i != inner.i) { EditorUpdateProcesses[inner.i] = EditorUpdateProcesses[outer.i]; EditorUpdatePaused[inner.i] = EditorUpdatePaused[outer.i]; EditorUpdateHeld[inner.i] = EditorUpdateHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextEditorUpdateProcessSlot; outer.i++) { EditorUpdateProcesses[outer.i] = null; EditorUpdatePaused[outer.i] = false; EditorUpdateHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } EditorUpdateCoroutines = _nextEditorUpdateProcessSlot = inner.i; outer.seg = inner.seg = Segment.EditorSlowUpdate; for (outer.i = inner.i = 0; outer.i < _nextEditorSlowUpdateProcessSlot; outer.i++) { if (EditorSlowUpdateProcesses[outer.i] != null) { if (outer.i != inner.i) { EditorSlowUpdateProcesses[inner.i] = EditorSlowUpdateProcesses[outer.i]; EditorUpdatePaused[inner.i] = EditorUpdatePaused[outer.i]; EditorUpdateHeld[inner.i] = EditorUpdateHeld[outer.i]; if (_indexToHandle.ContainsKey(inner)) { RemoveGraffiti(_indexToHandle[inner]); _handleToIndex.Remove(_indexToHandle[inner]); _indexToHandle.Remove(inner); } _handleToIndex[_indexToHandle[outer]] = inner; _indexToHandle.Add(inner, _indexToHandle[outer]); _indexToHandle.Remove(outer); } inner.i++; } } for (outer.i = inner.i; outer.i < _nextEditorSlowUpdateProcessSlot; outer.i++) { EditorSlowUpdateProcesses[outer.i] = null; EditorSlowUpdatePaused[outer.i] = false; EditorSlowUpdateHeld[outer.i] = false; if (_indexToHandle.ContainsKey(outer)) { RemoveGraffiti(_indexToHandle[outer]); _handleToIndex.Remove(_indexToHandle[outer]); _indexToHandle.Remove(outer); } } EditorSlowUpdateCoroutines = _nextEditorSlowUpdateProcessSlot = inner.i; } /// /// Run a new coroutine in the Update segment. /// /// The new coroutine's handle. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, GameObject gameObj) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, Segment.Update, gameObj == null ? (int?)null : gameObj.GetInstanceID(), null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment. /// /// The new coroutine's handle. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, int layer) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment. /// /// The new coroutine's handle. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, string tag) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, GameObject gameObj, string tag) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, Segment.Update, gameObj == null ? (int?)null : gameObj.GetInstanceID(), tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment. /// /// The new coroutine's handle. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, int layer, string tag) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, Segment segment) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, segment, null, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, Segment segment, GameObject gameObj) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, segment, gameObj == null ? (int?)null : gameObj.GetInstanceID(), null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, Segment segment, int layer) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, segment, layer, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, Segment segment, string tag) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, segment, null, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, Segment segment, GameObject gameObj, string tag) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, segment, gameObj == null ? (int?)null : gameObj.GetInstanceID(), tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public static CoroutineHandle RunCoroutine(IEnumerator coroutine, Segment segment, int layer, string tag) { return coroutine == null ? new CoroutineHandle() : Instance.RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine on this Timing instance in the Update segment. /// /// The new coroutine's handle. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance in the Update segment. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, GameObject gameObj) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, Segment.Update, gameObj == null ? (int?)null : gameObj.GetInstanceID(), null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance in the Update segment. /// /// The new coroutine's handle. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, int layer) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance in the Update segment. /// /// The new coroutine's handle. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, string tag) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance in the Update segment. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, GameObject gameObj, string tag) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, Segment.Update, gameObj == null ? (int?)null : gameObj.GetInstanceID(), tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance in the Update segment. /// /// The new coroutine's handle. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, int layer, string tag) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, Segment segment) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, segment, null, null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, Segment segment, GameObject gameObj) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, segment, gameObj == null ? (int?)null : gameObj.GetInstanceID(), null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, Segment segment, int layer) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, segment, layer, null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, Segment segment, string tag) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, segment, null, tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, Segment segment, GameObject gameObj, string tag) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, segment, gameObj == null ? (int?)null : gameObj.GetInstanceID(), tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine on this Timing instance. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. /// The coroutine's handle, which can be used for Wait and Kill operations. public CoroutineHandle RunCoroutineOnInstance(IEnumerator coroutine, Segment segment, int layer, string tag) { return coroutine == null ? new CoroutineHandle() : RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine in the Update segment, but not while the coroutine with the supplied handle is running. /// /// The new coroutine's handle. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, CoroutineHandle handle, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(handle); } else if (IsRunning(handle)) { if (behaviorOnCollision == SingletonBehavior.Abort) return handle; if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = Instance.RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(Instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, handle, false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, GameObject gameObj, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutine(coroutine) : RunCoroutineSingleton(coroutine, gameObj.GetInstanceID(), behaviorOnCollision); } /// /// Run a new coroutine in the Update segment with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// A layer to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, int layer, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(layer); } else if (Instance._layeredProcesses.ContainsKey(layer)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = Instance._layeredProcesses[layer].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(Instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, _instance._layeredProcesses[layer], false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. /// /// The new coroutine's handle. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(tag); } else if (Instance._taggedProcesses.ContainsKey(tag)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = Instance._taggedProcesses[tag].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = Instance.RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(Instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, _instance._taggedProcesses[tag], false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment with the supplied graffitti unless there is already one or more coroutines running with both that /// tag and layer. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, GameObject gameObj, string tag, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutineSingleton(coroutine, tag, behaviorOnCollision) : RunCoroutineSingleton(coroutine, gameObj.GetInstanceID(), tag, behaviorOnCollision); } /// /// Run a new coroutine in the Update segment with the supplied graffitti unless there is already one or more coroutines running with both that /// tag and layer. /// /// The new coroutine's handle. /// A layer to attach to the coroutine, and to check for existing instances. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, int layer, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(layer, tag); return Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(Instance._instanceID), true); } if (!Instance._taggedProcesses.ContainsKey(tag) || !Instance._layeredProcesses.ContainsKey(layer)) return Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(Instance._instanceID), true); if (behaviorOnCollision == SingletonBehavior.Abort) { var matchesEnum = Instance._taggedProcesses[tag].GetEnumerator(); while(matchesEnum.MoveNext()) if (_instance._processLayers.ContainsKey(matchesEnum.Current) && _instance._processLayers[matchesEnum.Current] == layer) return matchesEnum.Current; } if (behaviorOnCollision == SingletonBehavior.Wait) { List matches = new List(); var matchesEnum = Instance._taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) if (Instance._processLayers.ContainsKey(matchesEnum.Current) && Instance._processLayers[matchesEnum.Current] == layer) matches.Add(matchesEnum.Current); if(matches.Count > 0) { CoroutineHandle newCoroutineHandle = _instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, matches, false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine, but not while the coroutine with the supplied handle is running. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, CoroutineHandle handle, Segment segment, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(handle); } else if (IsRunning(handle)) { if (behaviorOnCollision == SingletonBehavior.Abort) return handle; if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = Instance.RunCoroutineInternal(coroutine, segment, null, null, new CoroutineHandle(Instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, handle, false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, segment, null, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, Segment segment, GameObject gameObj, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutine(coroutine, segment) : RunCoroutineSingleton(coroutine, segment, gameObj.GetInstanceID(), behaviorOnCollision); } /// /// Run a new coroutine with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// A layer to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, Segment segment, int layer, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(layer); } else if (Instance._layeredProcesses.ContainsKey(layer)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = Instance._layeredProcesses[layer].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = Instance.RunCoroutineInternal(coroutine, segment, layer, null, new CoroutineHandle(Instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, _instance._layeredProcesses[layer], false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, segment, layer, null, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, Segment segment, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(tag); } else if (Instance._taggedProcesses.ContainsKey(tag)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = Instance._taggedProcesses[tag].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = Instance.RunCoroutineInternal(coroutine, segment, null, tag, new CoroutineHandle(Instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, _instance._taggedProcesses[tag], false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, segment, null, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine with the supplied graffitti unless there is already one or more coroutines running with both that tag and layer. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, Segment segment, GameObject gameObj, string tag, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutineSingleton(coroutine, segment, tag, behaviorOnCollision) : RunCoroutineSingleton(coroutine, segment, gameObj.GetInstanceID(), tag, behaviorOnCollision); } /// /// Run a new coroutine with the supplied graffitti unless there is already one or more coroutines running with both that tag and layer. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// A layer to attach to the coroutine, and to check for existing instances. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public static CoroutineHandle RunCoroutineSingleton(IEnumerator coroutine, Segment segment, int layer, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutines(layer, tag); return Instance.RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(Instance._instanceID), true); } if (!Instance._taggedProcesses.ContainsKey(tag) || !Instance._layeredProcesses.ContainsKey(layer)) return Instance.RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(Instance._instanceID), true); if (behaviorOnCollision == SingletonBehavior.Abort) { var matchesEnum = Instance._taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) if (_instance._processLayers.ContainsKey(matchesEnum.Current) && _instance._processLayers[matchesEnum.Current] == layer) return matchesEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { List matches = new List(); var matchesEnum = Instance._taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) if (_instance._processLayers.ContainsKey(matchesEnum.Current) && _instance._processLayers[matchesEnum.Current] == layer) matches.Add(matchesEnum.Current); if (matches.Count > 0) { CoroutineHandle newCoroutineHandle = _instance.RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(_instance._instanceID), false); WaitForOtherHandles(newCoroutineHandle, matches, false); return newCoroutineHandle; } } return Instance.RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(Instance._instanceID), true); } /// /// Run a new coroutine in the Update segment, but not while the coroutine with the supplied handle is running. /// /// The new coroutine's handle. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, CoroutineHandle handle, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutinesOnInstance(handle); } else if (_handleToIndex.ContainsKey(handle) && !CoindexIsNull(_handleToIndex[handle])) { if (behaviorOnCollision == SingletonBehavior.Abort) return handle; if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(_instanceID), false); WaitForOtherHandles(newCoroutineHandle, handle, false); return newCoroutineHandle; } } return RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine in the Update segment with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, GameObject gameObj, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutineOnInstance(coroutine) : RunCoroutineSingletonOnInstance(coroutine, gameObj.GetInstanceID(), behaviorOnCollision); } /// /// Run a new coroutine in the Update segment with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// A layer to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, int layer, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutinesOnInstance(layer); } else if (_layeredProcesses.ContainsKey(layer)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = _layeredProcesses[layer].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(_instanceID), false); WaitForOtherHandles(newCoroutineHandle, _layeredProcesses[layer], false); return newCoroutineHandle; } } return RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. /// /// The new coroutine's handle. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutinesOnInstance(tag); } else if (_taggedProcesses.ContainsKey(tag)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = _taggedProcesses[tag].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(_instanceID), false); WaitForOtherHandles(newCoroutineHandle, _taggedProcesses[tag], false); return newCoroutineHandle; } } return RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine in the Update segment with the supplied graffitti unless there is already one or more coroutines running with both that /// tag and layer. /// /// The new coroutine's handle. /// The new coroutine will be put on a layer corresponding to this gameObject. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, GameObject gameObj, string tag, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutineSingletonOnInstance(coroutine, tag, behaviorOnCollision) : RunCoroutineSingletonOnInstance(coroutine, gameObj.GetInstanceID(), tag, behaviorOnCollision); } /// /// Run a new coroutine in the Update segment with the supplied graffitti unless there is already one or more coroutines running with both that /// tag and layer. /// /// The new coroutine's handle. /// A layer to attach to the coroutine, and to check for existing instances. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, int layer, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutinesOnInstance(layer, tag); return RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); } if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) return RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); if (behaviorOnCollision == SingletonBehavior.Abort) { var matchesEnum = _taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer) return matchesEnum.Current; } if (behaviorOnCollision == SingletonBehavior.Wait) { List matches = new List(); var matchesEnum = _taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer) matches.Add(matchesEnum.Current); if (matches.Count > 0) { CoroutineHandle newCoroutineHandle = RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), false); WaitForOtherHandles(newCoroutineHandle, matches, false); return newCoroutineHandle; } } return RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, Segment segment, GameObject gameObj, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutineOnInstance(coroutine, segment) : RunCoroutineSingletonOnInstance(coroutine, segment, gameObj.GetInstanceID(), behaviorOnCollision); } /// /// Run a new coroutine with the supplied layer unless there is already one or more coroutines running with that layer. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// A layer to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, Segment segment, int layer, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutinesOnInstance(layer); } else if (_layeredProcesses.ContainsKey(layer)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = _layeredProcesses[layer].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = RunCoroutineInternal(coroutine, segment, layer, null, new CoroutineHandle(_instanceID), false); WaitForOtherHandles(newCoroutineHandle, _layeredProcesses[layer], false); return newCoroutineHandle; } } return RunCoroutineInternal(coroutine, segment, layer, null, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, Segment segment, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutinesOnInstance(tag); } else if (_taggedProcesses.ContainsKey(tag)) { if (behaviorOnCollision == SingletonBehavior.Abort) { var indexEnum = _taggedProcesses[tag].GetEnumerator(); while (indexEnum.MoveNext()) if (IsRunning(indexEnum.Current)) return indexEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { CoroutineHandle newCoroutineHandle = RunCoroutineInternal(coroutine, segment, null, tag, new CoroutineHandle(_instanceID), false); WaitForOtherHandles(newCoroutineHandle, _taggedProcesses[tag], false); return newCoroutineHandle; } } return RunCoroutineInternal(coroutine, segment, null, tag, new CoroutineHandle(_instanceID), true); } /// /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// The new coroutine will be put on a layer corresponding to this gameObject. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, Segment segment, GameObject gameObj, string tag, SingletonBehavior behaviorOnCollision) { return gameObj == null ? RunCoroutineSingletonOnInstance(coroutine, segment, tag, behaviorOnCollision) : RunCoroutineSingletonOnInstance(coroutine, segment, gameObj.GetInstanceID(), tag, behaviorOnCollision); } /// /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. /// /// The new coroutine's handle. /// The segment that the coroutine should run in. /// A layer to attach to the coroutine, and to check for existing instances. /// A tag to attach to the coroutine, and to check for existing instances. /// Should this coroutine fail to start, overwrite, or wait for any coroutines to finish if any matches are /// currently running. /// The newly created or existing handle. public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator coroutine, Segment segment, int layer, string tag, SingletonBehavior behaviorOnCollision) { if (coroutine == null) return new CoroutineHandle(); if (behaviorOnCollision == SingletonBehavior.Overwrite) { KillCoroutinesOnInstance(layer, tag); return RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(_instanceID), true); } if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) return RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(_instanceID), true); if (behaviorOnCollision == SingletonBehavior.Abort) { var matchesEnum = _taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer) return matchesEnum.Current; } else if (behaviorOnCollision == SingletonBehavior.Wait) { List matches = new List(); var matchesEnum = _taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer) matches.Add(matchesEnum.Current); if (matches.Count > 0) { CoroutineHandle newCoroutineHandle = RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(_instanceID), false); WaitForOtherHandles(newCoroutineHandle, matches, false); return newCoroutineHandle; } } return RunCoroutineInternal(coroutine, segment, layer, tag, new CoroutineHandle(_instanceID), true); } private CoroutineHandle RunCoroutineInternal(IEnumerator coroutine, Segment segment, int? layer, string tag, CoroutineHandle handle, bool prewarm) { ProcessIndex slot = new ProcessIndex { seg = segment }; if (_handleToIndex.ContainsKey(handle)) { _indexToHandle.Remove(_handleToIndex[handle]); _handleToIndex.Remove(handle); } float currentLocalTime = localTime; float currentDeltaTime = deltaTime; CoroutineHandle cashedHandle = currentCoroutine; currentCoroutine = handle; try { switch (segment) { case Segment.Update: if (_nextUpdateProcessSlot >= UpdateProcesses.Length) { IEnumerator[] oldProcArray = UpdateProcesses; bool[] oldPausedArray = UpdatePaused; bool[] oldHeldArray = UpdateHeld; UpdateProcesses = new IEnumerator[UpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; UpdatePaused = new bool[UpdateProcesses.Length]; UpdateHeld = new bool[UpdateProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { UpdateProcesses[i] = oldProcArray[i]; UpdatePaused[i] = oldPausedArray[i]; UpdateHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastUpdateProcessSlot = _nextUpdateProcessSlot; slot.i = _nextUpdateProcessSlot++; UpdateProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); while (prewarm) { if (!UpdateProcesses[slot.i].MoveNext()) { if (_indexToHandle.ContainsKey(slot)) KillCoroutinesOnInstance(_indexToHandle[slot]); prewarm = false; } else if (UpdateProcesses[slot.i] != null && float.IsNaN(UpdateProcesses[slot.i].Current)) { if (ReplacementFunction != null) { UpdateProcesses[slot.i] = ReplacementFunction(UpdateProcesses[slot.i], _indexToHandle[slot]); ReplacementFunction = null; } prewarm = !UpdatePaused[slot.i] && !UpdateHeld[slot.i]; } else { prewarm = false; } } break; case Segment.FixedUpdate: if (_nextFixedUpdateProcessSlot >= FixedUpdateProcesses.Length) { IEnumerator[] oldProcArray = FixedUpdateProcesses; bool[] oldPausedArray = FixedUpdatePaused; bool[] oldHeldArray = FixedUpdateHeld; FixedUpdateProcesses = new IEnumerator[FixedUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; FixedUpdatePaused = new bool[FixedUpdateProcesses.Length]; FixedUpdateHeld = new bool[FixedUpdateProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { FixedUpdateProcesses[i] = oldProcArray[i]; FixedUpdatePaused[i] = oldPausedArray[i]; FixedUpdateHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastFixedUpdateProcessSlot = _nextFixedUpdateProcessSlot; slot.i = _nextFixedUpdateProcessSlot++; FixedUpdateProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); while (prewarm) { if (!FixedUpdateProcesses[slot.i].MoveNext()) { if (_indexToHandle.ContainsKey(slot)) KillCoroutinesOnInstance(_indexToHandle[slot]); prewarm = false; } else if (FixedUpdateProcesses[slot.i] != null && float.IsNaN(FixedUpdateProcesses[slot.i].Current)) { if (ReplacementFunction != null) { FixedUpdateProcesses[slot.i] = ReplacementFunction(FixedUpdateProcesses[slot.i], _indexToHandle[slot]); ReplacementFunction = null; } prewarm = !FixedUpdatePaused[slot.i] && !FixedUpdateHeld[slot.i]; } else { prewarm = false; } } break; case Segment.LateUpdate: if (_nextLateUpdateProcessSlot >= LateUpdateProcesses.Length) { IEnumerator[] oldProcArray = LateUpdateProcesses; bool[] oldPausedArray = LateUpdatePaused; bool[] oldHeldArray = LateUpdateHeld; LateUpdateProcesses = new IEnumerator[LateUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; LateUpdatePaused = new bool[LateUpdateProcesses.Length]; LateUpdateHeld = new bool[LateUpdateProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { LateUpdateProcesses[i] = oldProcArray[i]; LateUpdatePaused[i] = oldPausedArray[i]; LateUpdateHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastLateUpdateProcessSlot = _nextLateUpdateProcessSlot; slot.i = _nextLateUpdateProcessSlot++; LateUpdateProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); while (prewarm) { if (!LateUpdateProcesses[slot.i].MoveNext()) { if (_indexToHandle.ContainsKey(slot)) KillCoroutinesOnInstance(_indexToHandle[slot]); prewarm = false; } else if (LateUpdateProcesses[slot.i] != null && float.IsNaN(LateUpdateProcesses[slot.i].Current)) { if (ReplacementFunction != null) { LateUpdateProcesses[slot.i] = ReplacementFunction(LateUpdateProcesses[slot.i], _indexToHandle[slot]); ReplacementFunction = null; } prewarm = !LateUpdatePaused[slot.i] && !LateUpdateHeld[slot.i]; } else { prewarm = false; } } break; case Segment.SlowUpdate: if (_nextSlowUpdateProcessSlot >= SlowUpdateProcesses.Length) { IEnumerator[] oldProcArray = SlowUpdateProcesses; bool[] oldPausedArray = SlowUpdatePaused; bool[] oldHeldArray = SlowUpdateHeld; SlowUpdateProcesses = new IEnumerator[SlowUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; SlowUpdatePaused = new bool[SlowUpdateProcesses.Length]; SlowUpdateHeld = new bool[SlowUpdateProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { SlowUpdateProcesses[i] = oldProcArray[i]; SlowUpdatePaused[i] = oldPausedArray[i]; SlowUpdateHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastSlowUpdateProcessSlot = _nextSlowUpdateProcessSlot; slot.i = _nextSlowUpdateProcessSlot++; SlowUpdateProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); while (prewarm) { if (!SlowUpdateProcesses[slot.i].MoveNext()) { if (_indexToHandle.ContainsKey(slot)) KillCoroutinesOnInstance(_indexToHandle[slot]); prewarm = false; } else if (SlowUpdateProcesses[slot.i] != null && float.IsNaN(SlowUpdateProcesses[slot.i].Current)) { if (ReplacementFunction != null) { SlowUpdateProcesses[slot.i] = ReplacementFunction(SlowUpdateProcesses[slot.i], _indexToHandle[slot]); ReplacementFunction = null; } prewarm = !SlowUpdatePaused[slot.i] && !SlowUpdateHeld[slot.i]; } else { prewarm = false; } } break; case Segment.RealtimeUpdate: if (_nextRealtimeUpdateProcessSlot >= RealtimeUpdateProcesses.Length) { IEnumerator[] oldProcArray = RealtimeUpdateProcesses; bool[] oldPausedArray = RealtimeUpdatePaused; bool[] oldHeldArray = RealtimeUpdateHeld; RealtimeUpdateProcesses = new IEnumerator[RealtimeUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; RealtimeUpdatePaused = new bool[RealtimeUpdateProcesses.Length]; RealtimeUpdateHeld = new bool[RealtimeUpdateProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { RealtimeUpdateProcesses[i] = oldProcArray[i]; RealtimeUpdatePaused[i] = oldPausedArray[i]; RealtimeUpdateHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastRealtimeUpdateProcessSlot = _nextRealtimeUpdateProcessSlot; slot.i = _nextRealtimeUpdateProcessSlot++; RealtimeUpdateProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); while (prewarm) { if (!RealtimeUpdateProcesses[slot.i].MoveNext()) { if (_indexToHandle.ContainsKey(slot)) KillCoroutinesOnInstance(_indexToHandle[slot]); prewarm = false; } else if (RealtimeUpdateProcesses[slot.i] != null && float.IsNaN(RealtimeUpdateProcesses[slot.i].Current)) { if (ReplacementFunction != null) { RealtimeUpdateProcesses[slot.i] = ReplacementFunction(RealtimeUpdateProcesses[slot.i], _indexToHandle[slot]); ReplacementFunction = null; } prewarm = !RealtimeUpdatePaused[slot.i] && !RealtimeUpdateHeld[slot.i]; } else { prewarm = false; } } break; #if UNITY_EDITOR case Segment.EditorUpdate: if (!OnEditorStart()) return new CoroutineHandle(); if (handle.Key == 0) handle = new CoroutineHandle(_instanceID); if (_nextEditorUpdateProcessSlot >= EditorUpdateProcesses.Length) { IEnumerator[] oldProcArray = EditorUpdateProcesses; bool[] oldPausedArray = EditorUpdatePaused; bool[] oldHeldArray = EditorUpdateHeld; EditorUpdateProcesses = new IEnumerator[EditorUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; EditorUpdatePaused = new bool[EditorUpdateProcesses.Length]; EditorUpdateHeld = new bool[EditorUpdateProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { EditorUpdateProcesses[i] = oldProcArray[i]; EditorUpdatePaused[i] = oldPausedArray[i]; EditorUpdateHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastEditorUpdateProcessSlot = _nextEditorUpdateProcessSlot; slot.i = _nextEditorUpdateProcessSlot++; EditorUpdateProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); while (prewarm) { if (!EditorUpdateProcesses[slot.i].MoveNext()) { if (_indexToHandle.ContainsKey(slot)) KillCoroutinesOnInstance(_indexToHandle[slot]); prewarm = false; } else if (EditorUpdateProcesses[slot.i] != null && float.IsNaN(EditorUpdateProcesses[slot.i].Current)) { if (ReplacementFunction != null) { EditorUpdateProcesses[slot.i] = ReplacementFunction(EditorUpdateProcesses[slot.i], _indexToHandle[slot]); ReplacementFunction = null; } prewarm = !EditorUpdatePaused[slot.i] && !EditorUpdateHeld[slot.i]; } else { prewarm = false; } } break; case Segment.EditorSlowUpdate: if (!OnEditorStart()) return new CoroutineHandle(); if (handle.Key == 0) handle = new CoroutineHandle(_instanceID); if (_nextEditorSlowUpdateProcessSlot >= EditorSlowUpdateProcesses.Length) { IEnumerator[] oldProcArray = EditorSlowUpdateProcesses; bool[] oldPausedArray = EditorSlowUpdatePaused; bool[] oldHeldArray = EditorSlowUpdateHeld; EditorSlowUpdateProcesses = new IEnumerator[EditorSlowUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; EditorSlowUpdatePaused = new bool[EditorSlowUpdateProcesses.Length]; EditorSlowUpdateHeld = new bool[EditorSlowUpdateProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { EditorSlowUpdateProcesses[i] = oldProcArray[i]; EditorSlowUpdatePaused[i] = oldPausedArray[i]; EditorSlowUpdateHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastEditorSlowUpdateProcessSlot = _nextEditorSlowUpdateProcessSlot; slot.i = _nextEditorSlowUpdateProcessSlot++; EditorSlowUpdateProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); while (prewarm) { if (!EditorSlowUpdateProcesses[slot.i].MoveNext()) { if (_indexToHandle.ContainsKey(slot)) KillCoroutinesOnInstance(_indexToHandle[slot]); prewarm = false; } else if (EditorSlowUpdateProcesses[slot.i] != null && float.IsNaN(EditorSlowUpdateProcesses[slot.i].Current)) { if (ReplacementFunction != null) { EditorSlowUpdateProcesses[slot.i] = ReplacementFunction(EditorSlowUpdateProcesses[slot.i], _indexToHandle[slot]); ReplacementFunction = null; } prewarm = !EditorSlowUpdatePaused[slot.i] && !EditorSlowUpdateHeld[slot.i]; } else { prewarm = false; } } break; #endif case Segment.EndOfFrame: if (_nextEndOfFrameProcessSlot >= EndOfFrameProcesses.Length) { IEnumerator[] oldProcArray = EndOfFrameProcesses; bool[] oldPausedArray = EndOfFramePaused; bool[] oldHeldArray = EndOfFrameHeld; EndOfFrameProcesses = new IEnumerator[EndOfFrameProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; EndOfFramePaused = new bool[EndOfFrameProcesses.Length]; EndOfFrameHeld = new bool[EndOfFrameProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { EndOfFrameProcesses[i] = oldProcArray[i]; EndOfFramePaused[i] = oldPausedArray[i]; EndOfFrameHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastEndOfFrameProcessSlot = _nextEndOfFrameProcessSlot; slot.i = _nextEndOfFrameProcessSlot++; EndOfFrameProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); _eofWatcherHandle = RunCoroutineSingletonOnInstance(_EOFPumpWatcher(), _eofWatcherHandle, SingletonBehavior.Abort); break; case Segment.ManualTimeframe: if (_nextManualTimeframeProcessSlot >= ManualTimeframeProcesses.Length) { IEnumerator[] oldProcArray = ManualTimeframeProcesses; bool[] oldPausedArray = ManualTimeframePaused; bool[] oldHeldArray = ManualTimeframeHeld; ManualTimeframeProcesses = new IEnumerator[ManualTimeframeProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; ManualTimeframePaused = new bool[ManualTimeframeProcesses.Length]; ManualTimeframeHeld = new bool[ManualTimeframeProcesses.Length]; for (int i = 0; i < oldProcArray.Length; i++) { ManualTimeframeProcesses[i] = oldProcArray[i]; ManualTimeframePaused[i] = oldPausedArray[i]; ManualTimeframeHeld[i] = oldHeldArray[i]; } } if (UpdateTimeValues(slot.seg)) _lastManualTimeframeProcessSlot = _nextManualTimeframeProcessSlot; slot.i = _nextManualTimeframeProcessSlot++; ManualTimeframeProcesses[slot.i] = coroutine; if (null != tag) AddTagOnInstance(tag, handle); if (layer.HasValue) AddLayerOnInstance((int)layer, handle); _indexToHandle.Add(slot, handle); _handleToIndex.Add(handle, slot); break; default: handle = new CoroutineHandle(); break; } } catch (System.Exception ex) { Debug.LogException(ex); } localTime = currentLocalTime; deltaTime = currentDeltaTime; currentCoroutine = cashedHandle; return handle; } /// /// This will kill all coroutines running on the main MEC instance and reset the context. /// /// The number of coroutines that were killed. public static int KillCoroutines() { return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(); } /// /// This will kill all coroutines running on the current MEC instance and reset the context. /// /// The number of coroutines that were killed. public int KillCoroutinesOnInstance() { int retVal = _nextUpdateProcessSlot + _nextLateUpdateProcessSlot + _nextFixedUpdateProcessSlot + _nextSlowUpdateProcessSlot + _nextRealtimeUpdateProcessSlot + _nextEditorUpdateProcessSlot + _nextEditorSlowUpdateProcessSlot + _nextEndOfFrameProcessSlot + _nextManualTimeframeProcessSlot; UpdateProcesses = new IEnumerator[InitialBufferSizeLarge]; UpdatePaused = new bool[InitialBufferSizeLarge]; UpdateHeld = new bool[InitialBufferSizeLarge]; UpdateCoroutines = 0; _nextUpdateProcessSlot = 0; LateUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; LateUpdatePaused = new bool[InitialBufferSizeSmall]; LateUpdateHeld = new bool[InitialBufferSizeSmall]; LateUpdateCoroutines = 0; _nextLateUpdateProcessSlot = 0; FixedUpdateProcesses = new IEnumerator[InitialBufferSizeMedium]; FixedUpdatePaused = new bool[InitialBufferSizeMedium]; FixedUpdateHeld = new bool[InitialBufferSizeMedium]; FixedUpdateCoroutines = 0; _nextFixedUpdateProcessSlot = 0; SlowUpdateProcesses = new IEnumerator[InitialBufferSizeMedium]; SlowUpdatePaused = new bool[InitialBufferSizeMedium]; SlowUpdateHeld = new bool[InitialBufferSizeMedium]; SlowUpdateCoroutines = 0; _nextSlowUpdateProcessSlot = 0; RealtimeUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; RealtimeUpdatePaused = new bool[InitialBufferSizeSmall]; RealtimeUpdateHeld = new bool[InitialBufferSizeSmall]; RealtimeUpdateCoroutines = 0; _nextRealtimeUpdateProcessSlot = 0; EditorUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; EditorUpdatePaused = new bool[InitialBufferSizeSmall]; EditorUpdateHeld = new bool[InitialBufferSizeSmall]; EditorUpdateCoroutines = 0; _nextEditorUpdateProcessSlot = 0; EditorSlowUpdateProcesses = new IEnumerator[InitialBufferSizeSmall]; EditorSlowUpdatePaused = new bool[InitialBufferSizeSmall]; EditorSlowUpdateHeld = new bool[InitialBufferSizeSmall]; EditorSlowUpdateCoroutines = 0; _nextEditorSlowUpdateProcessSlot = 0; EndOfFrameProcesses = new IEnumerator[InitialBufferSizeSmall]; EndOfFramePaused = new bool[InitialBufferSizeSmall]; EndOfFrameHeld = new bool[InitialBufferSizeSmall]; EndOfFrameCoroutines = 0; _nextEndOfFrameProcessSlot = 0; ManualTimeframeProcesses = new IEnumerator[InitialBufferSizeSmall]; ManualTimeframePaused = new bool[InitialBufferSizeSmall]; ManualTimeframeHeld = new bool[InitialBufferSizeSmall]; ManualTimeframeCoroutines = 0; _nextManualTimeframeProcessSlot = 0; _processTags.Clear(); _taggedProcesses.Clear(); _processLayers.Clear(); _layeredProcesses.Clear(); _handleToIndex.Clear(); _indexToHandle.Clear(); _waitingTriggers.Clear(); _expansions = (ushort)((_expansions / 2) + 1); Links.Clear(); #if UNITY_EDITOR EditorApplication.update -= OnEditorUpdate; #endif return retVal; } /// /// Kills the instance of the coroutine handle if it exists. /// /// The handle of the coroutine to kill. /// The number of coroutines that were found and killed (0 or 1). public static int KillCoroutines(CoroutineHandle handle) { return ActiveInstances[handle.Key] != null ? GetInstance(handle.Key).KillCoroutinesOnInstance(handle) : 0; } /// /// Kills all the coroutines in a list of coroutine handles. /// /// A list of handles to be killed. /// The number of coroutines that were found and killed. public static int KillCoroutines(IEnumerable handles) { int count = 0; foreach (CoroutineHandle handle in handles) count += KillCoroutines(handle); return count; } /// /// Kills the instance of the coroutine handle on this Timing instance if it exists. /// /// The handle of the coroutine to kill. /// The number of coroutines that were found and killed (Normally 0 or 1). public int KillCoroutinesOnInstance(CoroutineHandle handle) { int count = 0; if (_handleToIndex.ContainsKey(handle)) { if (_waitingTriggers.ContainsKey(handle)) CloseWaitingProcess(handle); if (Nullify(handle)) count++; RemoveGraffiti(handle); } if (Links.ContainsKey(handle)) { var linksEnum = Links[handle].GetEnumerator(); Links.Remove(handle); while (linksEnum.MoveNext()) count += KillCoroutines(linksEnum.Current); } return count; } /// /// Kills all coroutines on the given layer. /// /// All coroutines on the layer corresponding with this GameObject will be killed. /// The number of coroutines that were found and killed. public static int KillCoroutines(GameObject gameObj) { return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(gameObj.GetInstanceID()); } /// /// Kills all coroutines on the given layer. /// /// All coroutines on the layer corresponding with this GameObject will be killed. /// The number of coroutines that were found and killed. public int KillCoroutinesOnInstance(GameObject gameObj) { return KillCoroutinesOnInstance(gameObj.GetInstanceID()); } /// /// Kills all coroutines on the given layer. /// /// All coroutines on this layer will be killed. /// The number of coroutines that were found and killed. public static int KillCoroutines(int layer) { return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(layer); } /// /// Kills all coroutines on the given layer. /// /// All coroutines on this layer will be killed. /// The number of coroutines that were found and killed. public int KillCoroutinesOnInstance(int layer) { int numberFound = 0; while (_layeredProcesses.ContainsKey(layer)) { var matchEnum = _layeredProcesses[layer].GetEnumerator(); matchEnum.MoveNext(); if (Nullify(matchEnum.Current)) { if (_waitingTriggers.ContainsKey(matchEnum.Current)) CloseWaitingProcess(matchEnum.Current); numberFound++; } RemoveGraffiti(matchEnum.Current); if (Links.ContainsKey(matchEnum.Current)) { var linksEnum = Links[matchEnum.Current].GetEnumerator(); Links.Remove(matchEnum.Current); while (linksEnum.MoveNext()) numberFound += KillCoroutines(linksEnum.Current); } } return numberFound; } /// /// Kills all coroutines that have the given tag. /// /// All coroutines with this tag will be killed. /// The number of coroutines that were found and killed. public static int KillCoroutines(string tag) { return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(tag); } /// /// Kills all coroutines that have the given tag. /// /// All coroutines with this tag will be killed. /// The number of coroutines that were found and killed. public int KillCoroutinesOnInstance(string tag) { if (tag == null) return 0; int numberFound = 0; while (_taggedProcesses.ContainsKey(tag)) { var matchEnum = _taggedProcesses[tag].GetEnumerator(); matchEnum.MoveNext(); if (Nullify(_handleToIndex[matchEnum.Current])) { if(_waitingTriggers.ContainsKey(matchEnum.Current)) CloseWaitingProcess(matchEnum.Current); numberFound++; } RemoveGraffiti(matchEnum.Current); if (Links.ContainsKey(matchEnum.Current)) { var linksEnum = Links[matchEnum.Current].GetEnumerator(); Links.Remove(matchEnum.Current); while (linksEnum.MoveNext()) numberFound += KillCoroutines(linksEnum.Current); } } return numberFound; } /// /// Kills all coroutines with the given tag on the given layer. /// /// All coroutines on the layer corresponding with this GameObject will be killed. /// All coroutines with this tag on the given layer will be killed. /// The number of coroutines that were found and killed. public static int KillCoroutines(GameObject gameObj, string tag) { return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(gameObj.GetInstanceID(), tag); } /// /// Kills all coroutines with the given tag on the given layer. /// /// All coroutines on the layer corresponding with this GameObject will be killed. /// All coroutines with this tag on the given layer will be killed. /// The number of coroutines that were found and killed. public int KillCoroutinesOnInstance(GameObject gameObj, string tag) { return KillCoroutinesOnInstance(gameObj.GetInstanceID(), tag); } /// /// Kills all coroutines with the given tag on the given layer. /// /// All coroutines on this layer with the given tag will be killed. /// All coroutines with this tag on the given layer will be killed. /// The number of coroutines that were found and killed. public static int KillCoroutines(int layer, string tag) { return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(layer, tag); } /// /// Kills all coroutines with the given tag on the given layer. /// /// All coroutines on this layer with the given tag will be killed. /// All coroutines with this tag on the given layer will be killed. /// The number of coroutines that were found and killed. public int KillCoroutinesOnInstance(int layer, string tag) { if (tag == null) return KillCoroutinesOnInstance(layer); if (!_layeredProcesses.ContainsKey(layer) || !_taggedProcesses.ContainsKey(tag)) return 0; int count = 0; var indexesEnum = _taggedProcesses[tag].GetEnumerator(); while(indexesEnum.MoveNext()) { if (CoindexIsNull(_handleToIndex[indexesEnum.Current]) || !_layeredProcesses[layer].Contains(indexesEnum.Current) || !Nullify(indexesEnum.Current)) continue; if (_waitingTriggers.ContainsKey(indexesEnum.Current)) CloseWaitingProcess(indexesEnum.Current); count++; RemoveGraffiti(indexesEnum.Current); if (Links.ContainsKey(indexesEnum.Current)) { var linksEnum = Links[indexesEnum.Current].GetEnumerator(); Links.Remove(indexesEnum.Current); while (linksEnum.MoveNext()) KillCoroutines(linksEnum.Current); } if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) break; indexesEnum = _taggedProcesses[tag].GetEnumerator(); } return count; } /// /// Retrieves the MEC manager that corresponds to the supplied instance id. /// /// The instance ID. /// The manager, or null if not found. public static Timing GetInstance(byte ID) { if (ID >= 0x10) return null; return ActiveInstances[ID]; } /// /// Use "yield return Timing.WaitForSeconds(time);" to wait for the specified number of seconds. /// /// Number of seconds to wait. public static float WaitForSeconds(float waitTime) { if (float.IsNaN(waitTime)) waitTime = 0f; return LocalTime + waitTime; } /// /// Use "yield return timingInstance.WaitForSecondsOnInstance(time);" to wait for the specified number of seconds. /// /// Number of seconds to wait. public float WaitForSecondsOnInstance(float waitTime) { if (float.IsNaN(waitTime)) waitTime = 0f; return localTime + waitTime; } private bool UpdateTimeValues(Segment segment) { switch (segment) { case Segment.Update: if (_currentUpdateFrame != Time.frameCount) { deltaTime = Time.deltaTime; _lastUpdateTime += deltaTime; localTime = _lastUpdateTime; _currentUpdateFrame = Time.frameCount; return true; } else { deltaTime = Time.deltaTime; localTime = _lastUpdateTime; return false; } case Segment.LateUpdate: if (_currentLateUpdateFrame != Time.frameCount) { deltaTime = Time.deltaTime; _lastLateUpdateTime += deltaTime; localTime = _lastLateUpdateTime; _currentLateUpdateFrame = Time.frameCount; return true; } else { deltaTime = Time.deltaTime; localTime = _lastLateUpdateTime; return false; } case Segment.FixedUpdate: deltaTime = Time.fixedDeltaTime; localTime = Time.fixedTime; if (_lastFixedUpdateTime + 0.0001f < Time.fixedTime) { _lastFixedUpdateTime = Time.fixedTime; return true; } return false; case Segment.SlowUpdate: if (_currentSlowUpdateFrame != Time.frameCount) { deltaTime = _lastSlowUpdateDeltaTime = Time.realtimeSinceStartup - _lastSlowUpdateTime; localTime = _lastSlowUpdateTime = Time.realtimeSinceStartup; _currentSlowUpdateFrame = Time.frameCount; return true; } else { localTime = _lastSlowUpdateTime; deltaTime = _lastSlowUpdateDeltaTime; return false; } case Segment.RealtimeUpdate: if (_currentRealtimeUpdateFrame != Time.frameCount) { deltaTime = Time.unscaledDeltaTime; _lastRealtimeUpdateTime += deltaTime; localTime = _lastRealtimeUpdateTime; _currentRealtimeUpdateFrame = Time.frameCount; return true; } else { deltaTime = Time.unscaledDeltaTime; localTime = _lastRealtimeUpdateTime; return false; } #if UNITY_EDITOR case Segment.EditorUpdate: if (_lastEditorUpdateTime + 0.0001 < EditorApplication.timeSinceStartup) { _lastEditorUpdateDeltaTime = (float)EditorApplication.timeSinceStartup - _lastEditorUpdateTime; if (_lastEditorUpdateDeltaTime > Time.maximumDeltaTime) _lastEditorUpdateDeltaTime = Time.maximumDeltaTime; deltaTime = _lastEditorUpdateDeltaTime; localTime = _lastEditorUpdateTime = (float)EditorApplication.timeSinceStartup; return true; } else { deltaTime = _lastEditorUpdateDeltaTime; localTime = _lastEditorUpdateTime; return false; } case Segment.EditorSlowUpdate: if (_lastEditorSlowUpdateTime + 0.0001 < EditorApplication.timeSinceStartup) { _lastEditorSlowUpdateDeltaTime = (float)EditorApplication.timeSinceStartup - _lastEditorSlowUpdateTime; deltaTime = _lastEditorSlowUpdateDeltaTime; localTime = _lastEditorSlowUpdateTime = (float)EditorApplication.timeSinceStartup; return true; } else { deltaTime = _lastEditorSlowUpdateDeltaTime; localTime = _lastEditorSlowUpdateTime; return false; } #endif case Segment.EndOfFrame: if (_currentEndOfFrameFrame != Time.frameCount) { deltaTime = Time.deltaTime; _lastEndOfFrameTime += deltaTime; localTime = _lastEndOfFrameTime; _currentEndOfFrameFrame = Time.frameCount; return true; } else { deltaTime = Time.deltaTime; localTime = _lastEndOfFrameTime; return false; } case Segment.ManualTimeframe: float timeCalculated = SetManualTimeframeTime == null ? Time.time : SetManualTimeframeTime(_lastManualTimeframeTime); if (_lastManualTimeframeTime + 0.0001 < timeCalculated && _lastManualTimeframeTime - 0.0001 > timeCalculated) { localTime = timeCalculated; deltaTime = localTime - _lastManualTimeframeTime; if (deltaTime > Time.maximumDeltaTime) deltaTime = Time.maximumDeltaTime; _lastManualTimeframeDeltaTime = deltaTime; _lastManualTimeframeTime = timeCalculated; return true; } else { deltaTime = _lastManualTimeframeDeltaTime; localTime = _lastManualTimeframeTime; return false; } } return true; } private float GetSegmentTime(Segment segment) { switch (segment) { case Segment.Update: if (_currentUpdateFrame == Time.frameCount) return _lastUpdateTime; else return _lastUpdateTime + Time.deltaTime; case Segment.LateUpdate: if (_currentUpdateFrame == Time.frameCount) return _lastLateUpdateTime; else return _lastLateUpdateTime + Time.deltaTime; case Segment.FixedUpdate: return Time.fixedTime; case Segment.SlowUpdate: return Time.realtimeSinceStartup; case Segment.RealtimeUpdate: if (_currentRealtimeUpdateFrame == Time.frameCount) return _lastRealtimeUpdateTime; else return _lastRealtimeUpdateTime + Time.unscaledDeltaTime; #if UNITY_EDITOR case Segment.EditorUpdate: case Segment.EditorSlowUpdate: return (float)EditorApplication.timeSinceStartup; #endif case Segment.EndOfFrame: if (_currentUpdateFrame == Time.frameCount) return _lastEndOfFrameTime; else return _lastEndOfFrameTime + Time.deltaTime; case Segment.ManualTimeframe: return _lastManualTimeframeTime; default: return 0f; } } /// /// This will pause all coroutines running on the main MEC instance until ResumeCoroutines is called. /// /// The number of coroutines that were paused. public static int PauseCoroutines() { return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(); } /// /// This will pause all coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. /// /// The number of coroutines that were paused. public int PauseCoroutinesOnInstance() { int count = 0; int i; for (i = 0;i < _nextUpdateProcessSlot;i++) { if (!UpdatePaused[i] && UpdateProcesses[i] != null) { count++; UpdatePaused[i] = true; if (UpdateProcesses[i].Current > GetSegmentTime(Segment.Update)) UpdateProcesses[i] = _InjectDelay(UpdateProcesses[i], UpdateProcesses[i].Current - GetSegmentTime(Segment.Update)); } } for (i = 0; i < _nextLateUpdateProcessSlot; i++) { if (!LateUpdatePaused[i] && LateUpdateProcesses[i] != null) { count++; LateUpdatePaused[i] = true; if (LateUpdateProcesses[i].Current > GetSegmentTime(Segment.LateUpdate)) LateUpdateProcesses[i] = _InjectDelay(LateUpdateProcesses[i], LateUpdateProcesses[i].Current - GetSegmentTime(Segment.LateUpdate)); } } for (i = 0; i < _nextFixedUpdateProcessSlot; i++) { if (!FixedUpdatePaused[i] && FixedUpdateProcesses[i] != null) { count++; FixedUpdatePaused[i] = true; if (FixedUpdateProcesses[i].Current > GetSegmentTime(Segment.FixedUpdate)) FixedUpdateProcesses[i] = _InjectDelay(FixedUpdateProcesses[i], FixedUpdateProcesses[i].Current - GetSegmentTime(Segment.FixedUpdate)); } } for (i = 0; i < _nextSlowUpdateProcessSlot; i++) { if (!SlowUpdatePaused[i] && SlowUpdateProcesses[i] != null) { count++; SlowUpdatePaused[i] = true; if (SlowUpdateProcesses[i].Current > GetSegmentTime(Segment.SlowUpdate)) SlowUpdateProcesses[i] = _InjectDelay(SlowUpdateProcesses[i], SlowUpdateProcesses[i].Current - GetSegmentTime(Segment.SlowUpdate)); } } for (i = 0; i < _nextRealtimeUpdateProcessSlot; i++) { if (!RealtimeUpdatePaused[i] && RealtimeUpdateProcesses[i] != null) { count++; RealtimeUpdatePaused[i] = true; if (RealtimeUpdateProcesses[i].Current > GetSegmentTime(Segment.RealtimeUpdate)) RealtimeUpdateProcesses[i] = _InjectDelay(RealtimeUpdateProcesses[i], RealtimeUpdateProcesses[i].Current - GetSegmentTime(Segment.RealtimeUpdate)); } } for (i = 0; i < _nextEditorUpdateProcessSlot; i++) { if (!EditorUpdatePaused[i] && EditorUpdateProcesses[i] != null) { count++; EditorUpdatePaused[i] = true; if (EditorUpdateProcesses[i].Current > GetSegmentTime(Segment.EditorUpdate)) EditorUpdateProcesses[i] = _InjectDelay(EditorUpdateProcesses[i], EditorUpdateProcesses[i].Current - GetSegmentTime(Segment.EditorUpdate)); } } for (i = 0; i < _nextEditorSlowUpdateProcessSlot; i++) { if (!EditorSlowUpdatePaused[i] && EditorSlowUpdateProcesses[i] != null) { count++; EditorSlowUpdatePaused[i] = true; if (EditorSlowUpdateProcesses[i].Current > GetSegmentTime(Segment.EditorSlowUpdate)) EditorSlowUpdateProcesses[i] = _InjectDelay(EditorSlowUpdateProcesses[i], EditorSlowUpdateProcesses[i].Current - GetSegmentTime(Segment.EditorSlowUpdate)); } } for (i = 0; i < _nextEndOfFrameProcessSlot; i++) { if (!EndOfFramePaused[i] && EndOfFrameProcesses[i] != null) { count++; EndOfFramePaused[i] = true; if (EndOfFrameProcesses[i].Current > GetSegmentTime(Segment.EndOfFrame)) EndOfFrameProcesses[i] = _InjectDelay(EndOfFrameProcesses[i], EndOfFrameProcesses[i].Current - GetSegmentTime(Segment.EndOfFrame)); } } for (i = 0; i < _nextManualTimeframeProcessSlot; i++) { if (!ManualTimeframePaused[i] && ManualTimeframeProcesses[i] != null) { count++; ManualTimeframePaused[i] = true; if (ManualTimeframeProcesses[i].Current > GetSegmentTime(Segment.ManualTimeframe)) ManualTimeframeProcesses[i] = _InjectDelay(ManualTimeframeProcesses[i], ManualTimeframeProcesses[i].Current - GetSegmentTime(Segment.ManualTimeframe)); } } var indexesEnum = Links.GetEnumerator(); while(indexesEnum.MoveNext()) { if (!_handleToIndex.ContainsKey(indexesEnum.Current.Key)) continue; var linksEnum = indexesEnum.Current.Value.GetEnumerator(); while(linksEnum.MoveNext()) count += PauseCoroutines(linksEnum.Current); } return count; } /// /// This will pause any matching coroutines until ResumeCoroutines is called. /// /// The handle of the coroutine to pause. /// The number of coroutines that were paused (0 or 1). public static int PauseCoroutines(CoroutineHandle handle) { return ActiveInstances[handle.Key] != null ? GetInstance(handle.Key).PauseCoroutinesOnInstance(handle) : 0; } /// /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. /// /// The handle of the coroutine to pause. /// The number of coroutines that were paused (Normally 0 or 1). public int PauseCoroutinesOnInstance(CoroutineHandle handle) { int count = 0; if (_handleToIndex.ContainsKey(handle) && !CoindexIsNull(_handleToIndex[handle]) && !SetPause(_handleToIndex[handle], true)) count++; if (Links.ContainsKey(handle)) { var links = Links[handle]; Links.Remove(handle); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += PauseCoroutines(linksEnum.Current); Links.Add(handle, links); } return count; } /// /// This will pause any matching coroutines until ResumeCoroutines is called. /// /// A list of handles to coroutines you want to pause. /// The number of coroutines that were paused. public static int PauseCoroutines(IEnumerable handles) { int total = 0; var handlesEnum = handles.GetEnumerator(); while (!handlesEnum.MoveNext()) total += PauseCoroutines(handlesEnum.Current); return total; } /// /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. /// /// All coroutines on the layer corresponding with this GameObject will be paused. /// The number of coroutines that were paused. public static int PauseCoroutines(GameObject gameObj) { return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(gameObj); } /// /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. /// /// All coroutines on the layer corresponding with this GameObject will be paused. /// The number of coroutines that were paused. public int PauseCoroutinesOnInstance(GameObject gameObj) { return gameObj == null ? 0 : PauseCoroutinesOnInstance(gameObj.GetInstanceID()); } /// /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. /// /// Any coroutines on the matching layer will be paused. /// The number of coroutines that were paused. public static int PauseCoroutines(int layer) { return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(layer); } /// /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. /// /// Any coroutines on the matching layer will be paused. /// The number of coroutines that were paused. public int PauseCoroutinesOnInstance(int layer) { if (!_layeredProcesses.ContainsKey(layer)) return 0; int count = 0; var matchesEnum = _layeredProcesses[layer].GetEnumerator(); while (matchesEnum.MoveNext()) { if (!CoindexIsNull(_handleToIndex[matchesEnum.Current]) && !SetPause(_handleToIndex[matchesEnum.Current], true)) count++; if (Links.ContainsKey(matchesEnum.Current)) { var links = Links[matchesEnum.Current]; Links.Remove(matchesEnum.Current); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += PauseCoroutines(linksEnum.Current); Links.Add(matchesEnum.Current, links); } } return count; } /// /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. /// /// Any coroutines with a matching tag will be paused. /// The number of coroutines that were paused. public static int PauseCoroutines(string tag) { return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(tag); } /// /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. /// /// Any coroutines with a matching tag will be paused. /// The number of coroutines that were paused. public int PauseCoroutinesOnInstance(string tag) { if (tag == null || !_taggedProcesses.ContainsKey(tag)) return 0; int count = 0; var matchesEnum = _taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) { if (!CoindexIsNull(_handleToIndex[matchesEnum.Current]) && !SetPause(_handleToIndex[matchesEnum.Current], true)) count++; if (Links.ContainsKey(matchesEnum.Current)) { var links = Links[matchesEnum.Current]; Links.Remove(matchesEnum.Current); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += PauseCoroutines(linksEnum.Current); Links.Add(matchesEnum.Current, links); } } return count; } /// /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. /// /// All coroutines on the layer corresponding with this GameObject will be paused. /// Any coroutines with a matching tag will be paused. /// The number of coroutines that were paused. public static int PauseCoroutines(GameObject gameObj, string tag) { return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(gameObj.GetInstanceID(), tag); } /// /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. /// /// All coroutines on the layer corresponding with this GameObject will be paused. /// Any coroutines with a matching tag will be paused. /// The number of coroutines that were paused. public int PauseCoroutinesOnInstance(GameObject gameObj, string tag) { return gameObj == null ? 0 : PauseCoroutinesOnInstance(gameObj.GetInstanceID(), tag); } /// /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. /// /// Any coroutines on the matching layer will be paused. /// Any coroutines with a matching tag will be paused. /// The number of coroutines that were paused. public static int PauseCoroutines(int layer, string tag) { return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(layer, tag); } /// /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. /// /// Any coroutines on the matching layer will be paused. /// Any coroutines with a matching tag will be paused. /// The number of coroutines that were paused. public int PauseCoroutinesOnInstance(int layer, string tag) { if (tag == null) return PauseCoroutinesOnInstance(layer); if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) return 0; int count = 0; var matchesEnum = _taggedProcesses[tag].GetEnumerator(); while (matchesEnum.MoveNext()) { if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer && !CoindexIsNull(_handleToIndex[matchesEnum.Current])) { if (!SetPause(_handleToIndex[matchesEnum.Current], true)) count++; if (Links.ContainsKey(matchesEnum.Current)) { var links = Links[matchesEnum.Current]; Links.Remove(matchesEnum.Current); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += PauseCoroutines(linksEnum.Current); Links.Add(matchesEnum.Current, links); } } } return count; } /// /// This resumes all coroutines on the current MEC instance if they are currently paused, otherwise it has /// no effect. /// /// The number of coroutines that were resumed. public static int ResumeCoroutines() { return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(); } /// /// This resumes all coroutines on this MEC instance if they are currently paused, otherwise it has no effect. /// /// The number of coroutines that were resumed. public int ResumeCoroutinesOnInstance() { int count = 0; ProcessIndex coindex; for (coindex.i = 0, coindex.seg = Segment.Update; coindex.i < _nextUpdateProcessSlot; coindex.i++) { if (UpdatePaused[coindex.i] && UpdateProcesses[coindex.i] != null) { UpdatePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.LateUpdate; coindex.i < _nextLateUpdateProcessSlot; coindex.i++) { if (LateUpdatePaused[coindex.i] && LateUpdateProcesses[coindex.i] != null) { LateUpdatePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.FixedUpdate; coindex.i < _nextFixedUpdateProcessSlot; coindex.i++) { if (FixedUpdatePaused[coindex.i] && FixedUpdateProcesses[coindex.i] != null) { FixedUpdatePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.SlowUpdate; coindex.i < _nextSlowUpdateProcessSlot; coindex.i++) { if (SlowUpdatePaused[coindex.i] && SlowUpdateProcesses[coindex.i] != null) { SlowUpdatePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.RealtimeUpdate; coindex.i < _nextRealtimeUpdateProcessSlot; coindex.i++) { if (RealtimeUpdatePaused[coindex.i] && RealtimeUpdateProcesses[coindex.i] != null) { RealtimeUpdatePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.EditorUpdate; coindex.i < _nextEditorUpdateProcessSlot; coindex.i++) { if (EditorUpdatePaused[coindex.i] && EditorUpdateProcesses[coindex.i] != null) { EditorUpdatePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.EditorSlowUpdate; coindex.i < _nextEditorSlowUpdateProcessSlot; coindex.i++) { if (EditorSlowUpdatePaused[coindex.i] && EditorSlowUpdateProcesses[coindex.i] != null) { EditorSlowUpdatePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.EndOfFrame; coindex.i < _nextEndOfFrameProcessSlot; coindex.i++) { if (EndOfFramePaused[coindex.i] && EndOfFrameProcesses[coindex.i] != null) { EndOfFramePaused[coindex.i] = false; count++; } } for (coindex.i = 0, coindex.seg = Segment.ManualTimeframe; coindex.i < _nextManualTimeframeProcessSlot; coindex.i++) { if (ManualTimeframePaused[coindex.i] && ManualTimeframeProcesses[coindex.i] != null) { ManualTimeframePaused[coindex.i] = false; count++; } } var indexesEnum = Links.GetEnumerator(); while (indexesEnum.MoveNext()) { if (!_handleToIndex.ContainsKey(indexesEnum.Current.Key)) continue; var linksEnum = indexesEnum.Current.Value.GetEnumerator(); while (linksEnum.MoveNext()) count += ResumeCoroutines(linksEnum.Current); } return count; } /// /// This will resume any matching coroutines. /// /// The handle of the coroutine to resume. /// The number of coroutines that were resumed. (Normally 0 or 1). public static int ResumeCoroutines(CoroutineHandle handle) { return ActiveInstances[handle.Key] != null ? GetInstance(handle.Key).ResumeCoroutinesOnInstance(handle) : 0; } /// /// This will resume any matching coroutines. /// /// A list of handles to coroutines you want to resume. /// The number of coroutines that were resumed. public static int ResumeCoroutines(IEnumerable handles) { int count = 0; var handlesEnum = handles.GetEnumerator(); while (!handlesEnum.MoveNext()) ResumeCoroutines(handlesEnum.Current); return count; } /// /// This will resume any matching coroutines running on this MEC instance. /// /// The handle of the coroutine to resume. /// The number of coroutines that were resumed. (Normally 0 or 1) public int ResumeCoroutinesOnInstance(CoroutineHandle handle) { int count = 0; if (_handleToIndex.ContainsKey(handle) && !CoindexIsNull(_handleToIndex[handle]) && SetPause(_handleToIndex[handle], false)) count++; if (Links.ContainsKey(handle)) { var links = Links[handle]; Links.Remove(handle); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += ResumeCoroutines(linksEnum.Current); Links.Add(handle, links); } return count; } /// /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has /// no effect. /// /// All coroutines on the layer corresponding with this GameObject will be resumed. /// The number of coroutines that were resumed. public static int ResumeCoroutines(GameObject gameObj) { return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(gameObj.GetInstanceID()); } /// /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. /// /// All coroutines on the layer corresponding with this GameObject will be resumed. /// The number of coroutines that were resumed. public int ResumeCoroutinesOnInstance(GameObject gameObj) { return gameObj == null ? 0 : ResumeCoroutinesOnInstance(gameObj.GetInstanceID()); } /// /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has /// no effect. /// /// Any coroutines previously paused on the matching layer will be resumend. /// The number of coroutines that were resumed. public static int ResumeCoroutines(int layer) { return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(layer); } /// /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. /// /// Any coroutines previously paused on the matching layer will be resumend. /// The number of coroutines that were resumed. public int ResumeCoroutinesOnInstance(int layer) { if (!_layeredProcesses.ContainsKey(layer)) return 0; int count = 0; var indexesEnum = _layeredProcesses[layer].GetEnumerator(); while (indexesEnum.MoveNext()) { if (!CoindexIsNull(_handleToIndex[indexesEnum.Current]) && SetPause(_handleToIndex[indexesEnum.Current], false)) { count++; } if (Links.ContainsKey(indexesEnum.Current)) { var links = Links[indexesEnum.Current]; Links.Remove(indexesEnum.Current); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += ResumeCoroutines(linksEnum.Current); Links.Add(indexesEnum.Current, links); } } return count; } /// /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has no effect. /// /// Any coroutines previously paused with a matching tag will be resumend. /// The number of coroutines that were resumed. public static int ResumeCoroutines(string tag) { return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(tag); } /// /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. /// /// Any coroutines previously paused with a matching tag will be resumend. /// The number of coroutines that were resumed. public int ResumeCoroutinesOnInstance(string tag) { if (tag == null || !_taggedProcesses.ContainsKey(tag)) return 0; int count = 0; var indexesEnum = _taggedProcesses[tag].GetEnumerator(); while (indexesEnum.MoveNext()) { if (!CoindexIsNull(_handleToIndex[indexesEnum.Current]) && SetPause(_handleToIndex[indexesEnum.Current], false)) { count++; } if (Links.ContainsKey(indexesEnum.Current)) { var links = Links[indexesEnum.Current]; Links.Remove(indexesEnum.Current); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += ResumeCoroutines(linksEnum.Current); Links.Add(indexesEnum.Current, links); } } return count; } /// /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has /// no effect. /// /// All coroutines on the layer corresponding with this GameObject will be resumed. /// Any coroutines previously paused with a matching tag will be resumend. /// The number of coroutines that were resumed. public static int ResumeCoroutines(GameObject gameObj, string tag) { return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(gameObj.GetInstanceID(), tag); } /// /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. /// /// All coroutines on the layer corresponding with this GameObject will be resumed. /// Any coroutines previously paused with a matching tag will be resumend. /// The number of coroutines that were resumed. public int ResumeCoroutinesOnInstance(GameObject gameObj, string tag) { return gameObj == null ? 0 : ResumeCoroutinesOnInstance(gameObj.GetInstanceID(), tag); } /// /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has /// no effect. /// /// Any coroutines previously paused on the matching layer will be resumend. /// Any coroutines previously paused with a matching tag will be resumend. /// The number of coroutines that were resumed. public static int ResumeCoroutines(int layer, string tag) { return _instance == null? 0 : _instance.ResumeCoroutinesOnInstance(layer, tag); } /// /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. /// /// Any coroutines previously paused on the matching layer will be resumend. /// Any coroutines previously paused with a matching tag will be resumend. /// The number of coroutines that were resumed. public int ResumeCoroutinesOnInstance(int layer, string tag) { if (tag == null) return ResumeCoroutinesOnInstance(layer); if (!_layeredProcesses.ContainsKey(layer) || !_taggedProcesses.ContainsKey(tag)) return 0; int count = 0; var indexesEnum = _taggedProcesses[tag].GetEnumerator(); while (indexesEnum.MoveNext()) { if (!CoindexIsNull(_handleToIndex[indexesEnum.Current]) && _layeredProcesses[layer].Contains(indexesEnum.Current)) { if (SetPause(_handleToIndex[indexesEnum.Current], false)) count++; if (Links.ContainsKey(indexesEnum.Current)) { var links = Links[indexesEnum.Current]; Links.Remove(indexesEnum.Current); var linksEnum = links.GetEnumerator(); while (linksEnum.MoveNext()) count += ResumeCoroutines(linksEnum.Current); Links.Add(indexesEnum.Current, links); } } } return count; } /// /// Returns the tag associated with the coroutine that the given handle points to, if it is running. /// /// The handle to the coroutine. /// The coroutine's tag, or null if there is no matching tag. public static string GetTag(CoroutineHandle handle) { Timing inst = GetInstance(handle.Key); return inst != null && inst._handleToIndex.ContainsKey(handle) && inst._processTags.ContainsKey(handle) ? inst._processTags[handle] : null; } /// /// Returns the layer associated with the coroutine that the given handle points to, if it is running. /// /// The handle to the coroutine. /// The coroutine's layer as a nullable integer, or null if there is no matching layer. public static int? GetLayer(CoroutineHandle handle) { Timing inst = GetInstance(handle.Key); return inst != null && inst._handleToIndex.ContainsKey(handle) && inst._processLayers.ContainsKey(handle) ? inst._processLayers[handle] : (int?)null; } /// /// Returns >NET's name for the coroutine that the handle points to. /// /// The handle to the coroutine. /// The underlying debug name of the coroutine, or info about the state of the coroutine. public static string GetDebugName(CoroutineHandle handle) { if (handle.Key == 0) return "Uninitialized handle"; Timing inst = GetInstance(handle.Key); if (inst == null) return "Invalid handle"; if (!inst._handleToIndex.ContainsKey(handle)) return "Expired coroutine"; return inst.CoindexPeek(inst._handleToIndex[handle]).ToString(); } /// /// Returns the segment that the coroutine with the given handle is running on. /// /// The handle to the coroutine. /// The coroutine's segment, or Segment.Invalid if it's not found. public static Segment GetSegment(CoroutineHandle handle) { Timing inst = GetInstance(handle.Key); return inst != null && inst._handleToIndex.ContainsKey(handle) ? inst._handleToIndex[handle].seg : Segment.Invalid; } /// /// Sets the coroutine that the handle points to to have the given tag. /// /// The handle to the coroutine. /// The new tag to assign, or null to clear the tag. /// If set to false then the tag will not be changed if the coroutine has an existing tag. /// Whether the tag was set successfully. public static bool SetTag(CoroutineHandle handle, string newTag, bool overwriteExisting = true) { Timing inst = GetInstance(handle.Key); if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle]) || (!overwriteExisting && inst._processTags.ContainsKey(handle))) return false; inst.RemoveTagOnInstance(handle); inst.AddTagOnInstance(newTag, handle); return true; } /// /// Sets the coroutine that the handle points to to have the given layer. /// /// The handle to the coroutine. /// The new tag to assign. /// If set to false then the tag will not be changed if the coroutine has an existing tag. /// Whether the layer was set successfully. public static bool SetLayer(CoroutineHandle handle, int newLayer, bool overwriteExisting = true) { Timing inst = GetInstance(handle.Key); if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle]) || (!overwriteExisting && inst._processLayers.ContainsKey(handle))) return false; inst.RemoveLayerOnInstance(handle); inst.AddLayerOnInstance(newLayer, handle); return true; } /// /// Sets the segment for the coroutine with the given handle. /// /// The handle to the coroutine. /// The new segment to run the coroutine in. /// Whether the segment was set successfully. public static bool SetSegment(CoroutineHandle handle, Segment newSegment) { Timing inst = GetInstance(handle.Key); if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle])) return false; // TODO, invistage whether leaving holes here causes problems. inst.RunCoroutineInternal(inst.CoindexExtract(inst._handleToIndex[handle]), newSegment, inst._processLayers.ContainsKey(handle) ? inst._processLayers[handle] : (int?)null, inst._processTags.ContainsKey(handle) ? inst._processTags[handle] : null, handle, false); return true; } /// /// Sets the coroutine that the handle points to to have the given tag. /// /// The handle to the coroutine. /// Whether the tag was removed successfully. public static bool RemoveTag(CoroutineHandle handle) { return SetTag(handle, null); } /// /// Sets the coroutine that the handle points to to have the given layer. /// /// The handle to the coroutine. /// Whether the layer was removed successfully. public static bool RemoveLayer(CoroutineHandle handle) { Timing inst = GetInstance(handle.Key); if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle])) return false; inst.RemoveLayerOnInstance(handle); return true; } /// /// Tests to see if the handle you have points to a valid coroutine that is currently running. /// /// The handle to test. /// Whether it's a valid coroutine. public static bool IsRunning(CoroutineHandle handle) { Timing inst = GetInstance(handle.Key); return inst != null && inst._handleToIndex.ContainsKey(handle) && !inst.CoindexIsNull(inst._handleToIndex[handle]); } /// /// Tests to see if the handle you have points to a coroutine that has not ended but is paused. /// /// The handle to test. /// Whether it's a paused coroutine. [System.Obsolete("Replaced with isAliveAndPaused.", false)] public static bool IsPaused(CoroutineHandle handle) { Timing inst = GetInstance(handle.Key); return inst != null && inst._handleToIndex.ContainsKey(handle) && !inst.CoindexIsNull(inst._handleToIndex[handle]) && !inst.CoindexIsPaused(inst._handleToIndex[handle]); } /// /// Tests to see if the handle you have points to a coroutine that has not ended but is paused. /// /// The handle to test. /// Whether it's a paused coroutine. public static bool IsAliveAndPaused(CoroutineHandle handle) { Timing inst = GetInstance(handle.Key); return inst != null && inst._handleToIndex.ContainsKey(handle) && !inst.CoindexIsNull(inst._handleToIndex[handle]) && inst.CoindexIsPaused(inst._handleToIndex[handle]); } private void AddTagOnInstance(string tag, CoroutineHandle handle) { _processTags.Add(handle, tag); if(_taggedProcesses.ContainsKey(tag)) _taggedProcesses[tag].Add(handle); else _taggedProcesses.Add(tag, new HashSet { handle }); } private void AddLayerOnInstance(int layer, CoroutineHandle handle) { _processLayers.Add(handle, layer); if (_layeredProcesses.ContainsKey(layer)) _layeredProcesses[layer].Add(handle); else _layeredProcesses.Add(layer, new HashSet { handle }); } private void RemoveTagOnInstance(CoroutineHandle handle) { if (_processTags.ContainsKey(handle)) { if (_taggedProcesses[_processTags[handle]].Count > 1) _taggedProcesses[_processTags[handle]].Remove(handle); else _taggedProcesses.Remove(_processTags[handle]); _processTags.Remove(handle); } } private void RemoveLayerOnInstance(CoroutineHandle handle) { if (_processLayers.ContainsKey(handle)) { if (_layeredProcesses[_processLayers[handle]].Count > 1) _layeredProcesses[_processLayers[handle]].Remove(handle); else _layeredProcesses.Remove(_processLayers[handle]); _processLayers.Remove(handle); } } private void RemoveGraffiti(CoroutineHandle handle) { if (_processLayers.ContainsKey(handle)) { if (_layeredProcesses[_processLayers[handle]].Count > 1) _layeredProcesses[_processLayers[handle]].Remove(handle); else _layeredProcesses.Remove(_processLayers[handle]); _processLayers.Remove(handle); } if (_processTags.ContainsKey(handle)) { if (_taggedProcesses[_processTags[handle]].Count > 1) _taggedProcesses[_processTags[handle]].Remove(handle); else _taggedProcesses.Remove(_processTags[handle]); _processTags.Remove(handle); } } private IEnumerator CoindexExtract(ProcessIndex coindex) { IEnumerator retVal; switch (coindex.seg) { case Segment.Update: retVal = UpdateProcesses[coindex.i]; UpdateProcesses[coindex.i] = null; return retVal; case Segment.FixedUpdate: retVal = FixedUpdateProcesses[coindex.i]; FixedUpdateProcesses[coindex.i] = null; return retVal; case Segment.LateUpdate: retVal = LateUpdateProcesses[coindex.i]; LateUpdateProcesses[coindex.i] = null; return retVal; case Segment.SlowUpdate: retVal = SlowUpdateProcesses[coindex.i]; SlowUpdateProcesses[coindex.i] = null; return retVal; case Segment.RealtimeUpdate: retVal = RealtimeUpdateProcesses[coindex.i]; RealtimeUpdateProcesses[coindex.i] = null; return retVal; case Segment.EditorUpdate: retVal = EditorUpdateProcesses[coindex.i]; EditorUpdateProcesses[coindex.i] = null; return retVal; case Segment.EditorSlowUpdate: retVal = EditorSlowUpdateProcesses[coindex.i]; EditorSlowUpdateProcesses[coindex.i] = null; return retVal; case Segment.EndOfFrame: retVal = EndOfFrameProcesses[coindex.i]; EndOfFrameProcesses[coindex.i] = null; return retVal; case Segment.ManualTimeframe: retVal = ManualTimeframeProcesses[coindex.i]; ManualTimeframeProcesses[coindex.i] = null; return retVal; default: return null; } } private bool CoindexIsNull(ProcessIndex coindex) { switch (coindex.seg) { case Segment.Update: return UpdateProcesses[coindex.i] == null; case Segment.FixedUpdate: return FixedUpdateProcesses[coindex.i] == null; case Segment.LateUpdate: return LateUpdateProcesses[coindex.i] == null; case Segment.SlowUpdate: return SlowUpdateProcesses[coindex.i] == null; case Segment.RealtimeUpdate: return RealtimeUpdateProcesses[coindex.i] == null; case Segment.EditorUpdate: return EditorUpdateProcesses[coindex.i] == null; case Segment.EditorSlowUpdate: return EditorSlowUpdateProcesses[coindex.i] == null; case Segment.EndOfFrame: return EndOfFrameProcesses[coindex.i] == null; case Segment.ManualTimeframe: return ManualTimeframeProcesses[coindex.i] == null; default: return true; } } private IEnumerator CoindexPeek(ProcessIndex coindex) { switch (coindex.seg) { case Segment.Update: return UpdateProcesses[coindex.i]; case Segment.FixedUpdate: return FixedUpdateProcesses[coindex.i]; case Segment.LateUpdate: return LateUpdateProcesses[coindex.i]; case Segment.SlowUpdate: return SlowUpdateProcesses[coindex.i]; case Segment.RealtimeUpdate: return RealtimeUpdateProcesses[coindex.i]; case Segment.EditorUpdate: return EditorUpdateProcesses[coindex.i]; case Segment.EditorSlowUpdate: return EditorSlowUpdateProcesses[coindex.i]; case Segment.EndOfFrame: return EndOfFrameProcesses[coindex.i]; case Segment.ManualTimeframe: return ManualTimeframeProcesses[coindex.i]; default: return null; } } /// Whether it was already null. private bool Nullify(CoroutineHandle handle) { return Nullify(_handleToIndex[handle]); } /// Whether it was already null. private bool Nullify(ProcessIndex coindex) { bool retVal; switch (coindex.seg) { case Segment.Update: retVal = UpdateProcesses[coindex.i] != null; UpdateProcesses[coindex.i] = null; return retVal; case Segment.FixedUpdate: retVal = FixedUpdateProcesses[coindex.i] != null; FixedUpdateProcesses[coindex.i] = null; return retVal; case Segment.LateUpdate: retVal = LateUpdateProcesses[coindex.i] != null; LateUpdateProcesses[coindex.i] = null; return retVal; case Segment.SlowUpdate: retVal = SlowUpdateProcesses[coindex.i] != null; SlowUpdateProcesses[coindex.i] = null; return retVal; case Segment.RealtimeUpdate: retVal = RealtimeUpdateProcesses[coindex.i] != null; RealtimeUpdateProcesses[coindex.i] = null; return retVal; case Segment.EditorUpdate: retVal = UpdateProcesses[coindex.i] != null; EditorUpdateProcesses[coindex.i] = null; return retVal; case Segment.EditorSlowUpdate: retVal = EditorSlowUpdateProcesses[coindex.i] != null; EditorSlowUpdateProcesses[coindex.i] = null; return retVal; case Segment.EndOfFrame: retVal = EndOfFrameProcesses[coindex.i] != null; EndOfFrameProcesses[coindex.i] = null; return retVal; case Segment.ManualTimeframe: retVal = ManualTimeframeProcesses[coindex.i] != null; ManualTimeframeProcesses[coindex.i] = null; return retVal; default: return false; } } private bool SetPause(ProcessIndex coindex, bool newPausedState) { if (CoindexPeek(coindex) == null) return false; bool isPaused; switch (coindex.seg) { case Segment.Update: isPaused = UpdatePaused[coindex.i]; UpdatePaused[coindex.i] = newPausedState; if (newPausedState && UpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) UpdateProcesses[coindex.i] = _InjectDelay(UpdateProcesses[coindex.i], UpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.FixedUpdate: isPaused = FixedUpdatePaused[coindex.i]; FixedUpdatePaused[coindex.i] = newPausedState; if (newPausedState && FixedUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) FixedUpdateProcesses[coindex.i] = _InjectDelay(FixedUpdateProcesses[coindex.i], FixedUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.LateUpdate: isPaused = LateUpdatePaused[coindex.i]; LateUpdatePaused[coindex.i] = newPausedState; if (newPausedState && LateUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) LateUpdateProcesses[coindex.i] = _InjectDelay(LateUpdateProcesses[coindex.i], LateUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.SlowUpdate: isPaused = SlowUpdatePaused[coindex.i]; SlowUpdatePaused[coindex.i] = newPausedState; if (newPausedState && SlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) SlowUpdateProcesses[coindex.i] = _InjectDelay(SlowUpdateProcesses[coindex.i], SlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.RealtimeUpdate: isPaused = RealtimeUpdatePaused[coindex.i]; RealtimeUpdatePaused[coindex.i] = newPausedState; if (newPausedState && RealtimeUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) RealtimeUpdateProcesses[coindex.i] = _InjectDelay(RealtimeUpdateProcesses[coindex.i], RealtimeUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.EditorUpdate: isPaused = EditorUpdatePaused[coindex.i]; EditorUpdatePaused[coindex.i] = newPausedState; if (newPausedState && EditorUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) EditorUpdateProcesses[coindex.i] = _InjectDelay(EditorUpdateProcesses[coindex.i], EditorUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.EditorSlowUpdate: isPaused = EditorSlowUpdatePaused[coindex.i]; EditorSlowUpdatePaused[coindex.i] = newPausedState; if (newPausedState && EditorSlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) EditorSlowUpdateProcesses[coindex.i] = _InjectDelay(EditorSlowUpdateProcesses[coindex.i], EditorSlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.EndOfFrame: isPaused = EndOfFramePaused[coindex.i]; EndOfFramePaused[coindex.i] = newPausedState; if (newPausedState && EndOfFrameProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) EndOfFrameProcesses[coindex.i] = _InjectDelay(EndOfFrameProcesses[coindex.i], EndOfFrameProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; case Segment.ManualTimeframe: isPaused = ManualTimeframePaused[coindex.i]; ManualTimeframePaused[coindex.i] = newPausedState; if (newPausedState && ManualTimeframeProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) ManualTimeframeProcesses[coindex.i] = _InjectDelay(ManualTimeframeProcesses[coindex.i], ManualTimeframeProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isPaused; default: return false; } } private bool SetHeld(ProcessIndex coindex, bool newHeldState) { if (CoindexPeek(coindex) == null) return false; bool isHeld; switch (coindex.seg) { case Segment.Update: isHeld = UpdateHeld[coindex.i]; UpdateHeld[coindex.i] = newHeldState; if (newHeldState && UpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) UpdateProcesses[coindex.i] = _InjectDelay(UpdateProcesses[coindex.i], UpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.FixedUpdate: isHeld = FixedUpdateHeld[coindex.i]; FixedUpdateHeld[coindex.i] = newHeldState; if (newHeldState && FixedUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) FixedUpdateProcesses[coindex.i] = _InjectDelay(FixedUpdateProcesses[coindex.i], FixedUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.LateUpdate: isHeld = LateUpdateHeld[coindex.i]; LateUpdateHeld[coindex.i] = newHeldState; if (newHeldState && LateUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) LateUpdateProcesses[coindex.i] = _InjectDelay(LateUpdateProcesses[coindex.i], LateUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.SlowUpdate: isHeld = SlowUpdateHeld[coindex.i]; SlowUpdateHeld[coindex.i] = newHeldState; if (newHeldState && SlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) SlowUpdateProcesses[coindex.i] = _InjectDelay(SlowUpdateProcesses[coindex.i], SlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.RealtimeUpdate: isHeld = RealtimeUpdateHeld[coindex.i]; RealtimeUpdateHeld[coindex.i] = newHeldState; if (newHeldState && RealtimeUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) RealtimeUpdateProcesses[coindex.i] = _InjectDelay(RealtimeUpdateProcesses[coindex.i], RealtimeUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.EditorUpdate: isHeld = EditorUpdateHeld[coindex.i]; EditorUpdateHeld[coindex.i] = newHeldState; if (newHeldState && EditorUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) EditorUpdateProcesses[coindex.i] = _InjectDelay(EditorUpdateProcesses[coindex.i], EditorUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.EditorSlowUpdate: isHeld = EditorSlowUpdateHeld[coindex.i]; EditorSlowUpdateHeld[coindex.i] = newHeldState; if (newHeldState && EditorSlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) EditorSlowUpdateProcesses[coindex.i] = _InjectDelay(EditorSlowUpdateProcesses[coindex.i], EditorSlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.EndOfFrame: isHeld = EndOfFrameHeld[coindex.i]; EndOfFrameHeld[coindex.i] = newHeldState; if (newHeldState && EndOfFrameProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) EndOfFrameProcesses[coindex.i] = _InjectDelay(EndOfFrameProcesses[coindex.i], EndOfFrameProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; case Segment.ManualTimeframe: isHeld = ManualTimeframeHeld[coindex.i]; ManualTimeframeHeld[coindex.i] = newHeldState; if (newHeldState && ManualTimeframeProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) ManualTimeframeProcesses[coindex.i] = _InjectDelay(ManualTimeframeProcesses[coindex.i], ManualTimeframeProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return isHeld; default: return false; } } private IEnumerator CreateHold(ProcessIndex coindex, IEnumerator coptr) { if (CoindexPeek(coindex) == null) return null; switch (coindex.seg) { case Segment.Update: UpdateHeld[coindex.i] = true; if (UpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(UpdateProcesses[coindex.i], UpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.FixedUpdate: FixedUpdateHeld[coindex.i] = true; if (FixedUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(FixedUpdateProcesses[coindex.i], FixedUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.LateUpdate: LateUpdateHeld[coindex.i] = true; if (LateUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(LateUpdateProcesses[coindex.i], LateUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.SlowUpdate: SlowUpdateHeld[coindex.i] = true; if (SlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(SlowUpdateProcesses[coindex.i], SlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.RealtimeUpdate: RealtimeUpdateHeld[coindex.i] = true; if (RealtimeUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(RealtimeUpdateProcesses[coindex.i], RealtimeUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.EditorUpdate: EditorUpdateHeld[coindex.i] = true; if (EditorUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(EditorUpdateProcesses[coindex.i], EditorUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.EditorSlowUpdate: EditorSlowUpdateHeld[coindex.i] = true; if (EditorSlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(EditorSlowUpdateProcesses[coindex.i], EditorSlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.EndOfFrame: EndOfFrameHeld[coindex.i] = true; if (EndOfFrameProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(EndOfFrameProcesses[coindex.i], EndOfFrameProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; case Segment.ManualTimeframe: ManualTimeframeHeld[coindex.i] = true; if (ManualTimeframeProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) coptr = _InjectDelay(ManualTimeframeProcesses[coindex.i], ManualTimeframeProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); return coptr; default: return coptr; } } private bool CoindexIsPaused(ProcessIndex coindex) { switch (coindex.seg) { case Segment.Update: return UpdatePaused[coindex.i]; case Segment.FixedUpdate: return FixedUpdatePaused[coindex.i]; case Segment.LateUpdate: return LateUpdatePaused[coindex.i]; case Segment.SlowUpdate: return SlowUpdatePaused[coindex.i]; case Segment.RealtimeUpdate: return RealtimeUpdatePaused[coindex.i]; case Segment.EditorUpdate: return EditorUpdatePaused[coindex.i]; case Segment.EditorSlowUpdate: return EditorSlowUpdatePaused[coindex.i]; case Segment.EndOfFrame: return EndOfFramePaused[coindex.i]; case Segment.ManualTimeframe: return ManualTimeframePaused[coindex.i]; default: return false; } } private bool CoindexIsHeld(ProcessIndex coindex) { switch (coindex.seg) { case Segment.Update: return UpdateHeld[coindex.i]; case Segment.FixedUpdate: return FixedUpdateHeld[coindex.i]; case Segment.LateUpdate: return LateUpdateHeld[coindex.i]; case Segment.SlowUpdate: return SlowUpdateHeld[coindex.i]; case Segment.RealtimeUpdate: return RealtimeUpdateHeld[coindex.i]; case Segment.EditorUpdate: return EditorUpdateHeld[coindex.i]; case Segment.EditorSlowUpdate: return EditorSlowUpdateHeld[coindex.i]; case Segment.EndOfFrame: return EndOfFrameHeld[coindex.i]; case Segment.ManualTimeframe: return ManualTimeframeHeld[coindex.i]; default: return false; } } private void CoindexReplace(ProcessIndex coindex, IEnumerator replacement) { switch (coindex.seg) { case Segment.Update: UpdateProcesses[coindex.i] = replacement; return; case Segment.FixedUpdate: FixedUpdateProcesses[coindex.i] = replacement; return; case Segment.LateUpdate: LateUpdateProcesses[coindex.i] = replacement; return; case Segment.SlowUpdate: SlowUpdateProcesses[coindex.i] = replacement; return; case Segment.RealtimeUpdate: RealtimeUpdateProcesses[coindex.i] = replacement; return; case Segment.EditorUpdate: EditorUpdateProcesses[coindex.i] = replacement; return; case Segment.EditorSlowUpdate: EditorSlowUpdateProcesses[coindex.i] = replacement; return; case Segment.EndOfFrame: EndOfFrameProcesses[coindex.i] = replacement; return; case Segment.ManualTimeframe: ManualTimeframeProcesses[coindex.i] = replacement; return; } } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. public static float WaitUntilDone(IEnumerator newCoroutine) { return WaitUntilDone(RunCoroutine(newCoroutine), true); } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. public static float WaitUntilDone(IEnumerator newCoroutine, string tag) { return WaitUntilDone(RunCoroutine(newCoroutine, tag), true); } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. public static float WaitUntilDone(IEnumerator newCoroutine, int layer) { return WaitUntilDone(RunCoroutine(newCoroutine, layer), true); } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. public static float WaitUntilDone(IEnumerator newCoroutine, int layer, string tag) { return WaitUntilDone(RunCoroutine(newCoroutine, layer, tag), true); } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. /// The segment that the new coroutine should run in. public static float WaitUntilDone(IEnumerator newCoroutine, Segment segment) { return WaitUntilDone(RunCoroutine(newCoroutine, segment), true); } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. /// The segment that the new coroutine should run in. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. public static float WaitUntilDone(IEnumerator newCoroutine, Segment segment, string tag) { return WaitUntilDone(RunCoroutine(newCoroutine, segment, tag), true); } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. /// The segment that the new coroutine should run in. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. public static float WaitUntilDone(IEnumerator newCoroutine, Segment segment, int layer) { return WaitUntilDone(RunCoroutine(newCoroutine, segment, layer), true); } /// /// Use the command "yield return Timing.WaitUntilDone(newCoroutine);" to start a new coroutine and pause the /// current one until it finishes. /// /// The coroutine to pause for. /// The segment that the new coroutine should run in. /// An optional layer to attach to the coroutine which can later be used to identify this coroutine. /// An optional tag to attach to the coroutine which can later be used to identify this coroutine. public static float WaitUntilDone(IEnumerator newCoroutine, Segment segment, int layer, string tag) { return WaitUntilDone(RunCoroutine(newCoroutine, segment, layer, tag), true); } /// /// Use the command "yield return Timing.WaitUntilDone(otherCoroutine);" to pause the current /// coroutine until otherCoroutine is done. /// /// The coroutine to pause for. public static float WaitUntilDone(CoroutineHandle otherCoroutine) { return WaitUntilDone(otherCoroutine, true); } /// /// Use the command "yield return Timing.WaitUntilDone(otherCoroutine, false);" to pause the current /// coroutine until otherCoroutine is done, supressing warnings. /// /// The coroutine to pause for. /// Post a warning if no hold action was actually performed. public static float WaitUntilDone(CoroutineHandle otherCoroutine, bool warnOnIssue) { Timing inst = GetInstance(otherCoroutine.Key); if (inst != null && inst._handleToIndex.ContainsKey(otherCoroutine)) { if (inst.CoindexIsNull(inst._handleToIndex[otherCoroutine])) return 0f; if (!inst._waitingTriggers.ContainsKey(otherCoroutine)) { inst.CoindexReplace(inst._handleToIndex[otherCoroutine], inst._StartWhenDone(otherCoroutine, inst.CoindexPeek(inst._handleToIndex[otherCoroutine]))); inst._waitingTriggers.Add(otherCoroutine, new HashSet()); } if (inst.currentCoroutine == otherCoroutine) { Assert.IsFalse(warnOnIssue, "A coroutine cannot wait for itself."); return WaitForOneFrame; } if (!inst.currentCoroutine.IsValid) { Assert.IsFalse(warnOnIssue, "The two coroutines are not running on the same MEC instance."); return WaitForOneFrame; } inst._waitingTriggers[otherCoroutine].Add(inst.currentCoroutine); if (!inst._allWaiting.Contains(inst.currentCoroutine)) inst._allWaiting.Add(inst.currentCoroutine); inst.SetHeld(inst._handleToIndex[inst.currentCoroutine], true); inst.SwapToLast(otherCoroutine, inst.currentCoroutine); return float.NaN; } Assert.IsFalse(warnOnIssue, "WaitUntilDone cannot hold, the coroutine handle that was passed in is invalid: " + otherCoroutine); return 0f; } /// /// This will pause one coroutine until another coroutine finishes running. Note: This is NOT used with a yield return statement. /// /// The coroutine that should be paused. /// The coroutine that will be waited for. /// Whether a warning should be logged if there is a problem. public static void WaitForOtherHandles(CoroutineHandle handle, CoroutineHandle otherHandle, bool warnOnIssue = true) { if (!IsRunning(handle) || !IsRunning(otherHandle)) return; if(handle == otherHandle) { Assert.IsFalse(warnOnIssue, "A coroutine cannot wait for itself."); return; } if(handle.Key != otherHandle.Key) { Assert.IsFalse(warnOnIssue, "A coroutine cannot wait for another coroutine on a different MEC instance."); return; } Timing inst = GetInstance(handle.Key); if (inst != null && inst._handleToIndex.ContainsKey(handle) && inst._handleToIndex.ContainsKey(otherHandle) && !inst.CoindexIsNull(inst._handleToIndex[otherHandle])) { if (!inst._waitingTriggers.ContainsKey(otherHandle)) { inst.CoindexReplace(inst._handleToIndex[otherHandle], inst._StartWhenDone(otherHandle, inst.CoindexPeek(inst._handleToIndex[otherHandle]))); inst._waitingTriggers.Add(otherHandle, new HashSet()); } inst._waitingTriggers[otherHandle].Add(handle); if (!inst._allWaiting.Contains(handle)) inst._allWaiting.Add(handle); inst.SetHeld(inst._handleToIndex[handle], true); inst.SwapToLast(otherHandle, handle); } } /// /// This will pause one coroutine until the other coroutines finish running. Note: This is NOT used with a yield return statement. /// /// The coroutine that should be paused. /// A list of coroutines to be waited for. /// Whether a warning should be logged if there is a problem. public static void WaitForOtherHandles(CoroutineHandle handle, IEnumerable otherHandles, bool warnOnIssue = true) { if (!IsRunning(handle)) return; Timing inst = GetInstance(handle.Key); var othersEnum = otherHandles.GetEnumerator(); while(othersEnum.MoveNext()) { if(!IsRunning(othersEnum.Current)) continue; if (handle == othersEnum.Current) { Assert.IsFalse(warnOnIssue, "A coroutine cannot wait for itself."); continue; } if (handle.Key != othersEnum.Current.Key) { Assert.IsFalse(warnOnIssue, "A coroutine cannot wait for another coroutine on a different MEC instance."); continue; } if (!inst._waitingTriggers.ContainsKey(othersEnum.Current)) { inst.CoindexReplace(inst._handleToIndex[othersEnum.Current], inst._StartWhenDone(othersEnum.Current, inst.CoindexPeek(inst._handleToIndex[othersEnum.Current]))); inst._waitingTriggers.Add(othersEnum.Current, new HashSet()); } inst._waitingTriggers[othersEnum.Current].Add(handle); if (!inst._allWaiting.Contains(handle)) inst._allWaiting.Add(handle); inst.SetHeld(inst._handleToIndex[handle], true); inst.SwapToLast(othersEnum.Current, handle); } } private void SwapToLast(CoroutineHandle firstHandle, CoroutineHandle lastHandle) { if (firstHandle.Key != lastHandle.Key) return; ProcessIndex firstIndex = _handleToIndex[firstHandle]; ProcessIndex lastIndex = _handleToIndex[lastHandle]; if (firstIndex.seg != lastIndex.seg || firstIndex.i <= lastIndex.i) return; IEnumerator tempCoptr = CoindexPeek(firstIndex); CoindexReplace(firstIndex, CoindexPeek(lastIndex)); CoindexReplace(lastIndex, tempCoptr); _indexToHandle[firstIndex] = lastHandle; _indexToHandle[lastIndex] = firstHandle; _handleToIndex[firstHandle] = lastIndex; _handleToIndex[lastHandle] = firstIndex; bool tmpB = SetPause(firstIndex, CoindexIsPaused(lastIndex)); SetPause(lastIndex, tmpB); tmpB = SetHeld(firstIndex, CoindexIsHeld(lastIndex)); SetHeld(lastIndex, tmpB); if (_waitingTriggers.ContainsKey(lastHandle)) { var trigsEnum = _waitingTriggers[lastHandle].GetEnumerator(); while (trigsEnum.MoveNext()) SwapToLast(lastHandle, trigsEnum.Current); } if (_allWaiting.Contains(firstHandle)) { var keyEnum = _waitingTriggers.GetEnumerator(); while (keyEnum.MoveNext()) { var valueEnum = keyEnum.Current.Value.GetEnumerator(); while (valueEnum.MoveNext()) if (valueEnum.Current == firstHandle) SwapToLast(keyEnum.Current.Key, firstHandle); } } } private IEnumerator _StartWhenDone(CoroutineHandle handle, IEnumerator proc) { if (!_waitingTriggers.ContainsKey(handle)) yield break; try { if (proc.Current > localTime) yield return proc.Current; while (proc.MoveNext()) yield return proc.Current; } finally { CloseWaitingProcess(handle); } } private void CloseWaitingProcess(CoroutineHandle handle) { if (!_waitingTriggers.ContainsKey(handle)) return; var tasksEnum = _waitingTriggers[handle].GetEnumerator(); _waitingTriggers.Remove(handle); while (tasksEnum.MoveNext()) { if (_handleToIndex.ContainsKey(tasksEnum.Current) && !HandleIsInWaitingList(tasksEnum.Current)) { SetHeld(_handleToIndex[tasksEnum.Current], false); _allWaiting.Remove(tasksEnum.Current); } } } private bool HandleIsInWaitingList(CoroutineHandle handle) { var triggersEnum = _waitingTriggers.GetEnumerator(); while (triggersEnum.MoveNext()) if (triggersEnum.Current.Value.Contains(handle)) return true; return false; } private static IEnumerator ReturnTmpRefForRepFunc(IEnumerator coptr, CoroutineHandle handle) { return _tmpRef as IEnumerator; } #if !UNITY_2018_3_OR_NEWER /// /// Use the command "yield return Timing.WaitUntilDone(wwwObject);" to pause the current /// coroutine until the wwwObject is done. /// /// The www object to pause for. public static float WaitUntilDone(WWW wwwObject) { if (wwwObject == null || wwwObject.isDone) return 0f; _tmpRef = wwwObject; ReplacementFunction = WaitUntilDoneWwwHelper; return float.NaN; } private static IEnumerator WaitUntilDoneWwwHelper(IEnumerator coptr, CoroutineHandle handle) { return _StartWhenDone(_tmpRef as WWW, coptr); } private static IEnumerator _StartWhenDone(WWW wwwObject, IEnumerator pausedProc) { while (!wwwObject.isDone) yield return WaitForOneFrame; _tmpRef = pausedProc; ReplacementFunction = ReturnTmpRefForRepFunc; yield return float.NaN; } #endif /// /// Use the command "yield return Timing.WaitUntilDone(operation);" to pause the current /// coroutine until the operation is done. /// /// The operation variable returned. public static float WaitUntilDone(AsyncOperation operation) { if (operation == null || operation.isDone) return float.NaN; CoroutineHandle handle = CurrentCoroutine; Timing inst = GetInstance(CurrentCoroutine.Key); if (inst == null) return float.NaN; _tmpRef = _StartWhenDone(operation, inst.CoindexPeek(inst._handleToIndex[handle])); ReplacementFunction = ReturnTmpRefForRepFunc; return float.NaN; } private static IEnumerator _StartWhenDone(AsyncOperation operation, IEnumerator pausedProc) { while (!operation.isDone) yield return WaitForOneFrame; _tmpRef = pausedProc; ReplacementFunction = ReturnTmpRefForRepFunc; yield return float.NaN; } /// /// Use the command "yield return Timing.WaitUntilDone(operation);" to pause the current /// coroutine until the operation is done. /// /// The operation variable returned. public static float WaitUntilDone(CustomYieldInstruction operation) { if (operation == null || !operation.keepWaiting) return float.NaN; CoroutineHandle handle = CurrentCoroutine; Timing inst = GetInstance(CurrentCoroutine.Key); if (inst == null) return float.NaN; _tmpRef = _StartWhenDone(operation, inst.CoindexPeek(inst._handleToIndex[handle])); ReplacementFunction = ReturnTmpRefForRepFunc; return float.NaN; } private static IEnumerator _StartWhenDone(CustomYieldInstruction operation, IEnumerator pausedProc) { while (operation.keepWaiting) yield return WaitForOneFrame; _tmpRef = pausedProc; ReplacementFunction = ReturnTmpRefForRepFunc; yield return float.NaN; } /// /// Use the command "yield return Timing.WaitUntilTrue(evaluatorFunc);" to pause the current /// coroutine until the evaluator function returns true. /// /// The evaluator function. public static float WaitUntilTrue(System.Func evaluatorFunc) { if (evaluatorFunc == null || evaluatorFunc()) return float.NaN; _tmpRef = evaluatorFunc; ReplacementFunction = WaitUntilTrueHelper; return float.NaN; } private static IEnumerator WaitUntilTrueHelper(IEnumerator coptr, CoroutineHandle handle) { return _StartWhenDone(_tmpRef as System.Func, false, coptr); } /// /// Use the command "yield return Timing.WaitUntilFalse(evaluatorFunc);" to pause the current /// coroutine until the evaluator function returns false. /// /// The evaluator function. public static float WaitUntilFalse(System.Func evaluatorFunc) { if (evaluatorFunc == null || !evaluatorFunc()) return float.NaN; _tmpRef = evaluatorFunc; ReplacementFunction = WaitUntilFalseHelper; return float.NaN; } private static IEnumerator WaitUntilFalseHelper(IEnumerator coptr, CoroutineHandle handle) { return _StartWhenDone(_tmpRef as System.Func, true, coptr); } private static IEnumerator _StartWhenDone(System.Func evaluatorFunc, bool continueOn, IEnumerator pausedProc) { while (evaluatorFunc() == continueOn) yield return WaitForOneFrame; _tmpRef = pausedProc; ReplacementFunction = ReturnTmpRefForRepFunc; yield return float.NaN; } private IEnumerator _InjectDelay(IEnumerator proc, float waitTime) { yield return WaitForSecondsOnInstance(waitTime); _tmpRef = proc; ReplacementFunction = ReturnTmpRefForRepFunc; yield return float.NaN; } /// /// Keeps this coroutine from executing until UnlockCoroutine is called with a matching key. /// /// The handle to the coroutine to be locked. /// The key to use. A new key can be generated by calling "new CoroutineHandle(0)". /// Whether the lock was successful. public bool LockCoroutine(CoroutineHandle coroutine, CoroutineHandle key) { if (coroutine.Key != _instanceID || key == new CoroutineHandle() || key.Key != 0) return false; if (!_waitingTriggers.ContainsKey(key)) _waitingTriggers.Add(key, new HashSet { coroutine }); else _waitingTriggers[key].Add(coroutine); _allWaiting.Add(coroutine); SetHeld(_handleToIndex[coroutine], true); return true; } /// /// Unlocks a coroutine that has been locked, so long as the key matches. /// /// The handle to the coroutine to be unlocked. /// The key that the coroutine was previously locked with. /// Whether the coroutine was successfully unlocked. public bool UnlockCoroutine(CoroutineHandle coroutine, CoroutineHandle key) { if (coroutine.Key != _instanceID || key == new CoroutineHandle() || !_handleToIndex.ContainsKey(coroutine) || !_waitingTriggers.ContainsKey(key)) return false; if (_waitingTriggers[key].Count == 1) _waitingTriggers.Remove(key); else _waitingTriggers[key].Remove(coroutine); if (!HandleIsInWaitingList(coroutine)) { SetHeld(_handleToIndex[coroutine], false); _allWaiting.Remove(coroutine); } return true; } /// /// This will create a one way link between two handles. If the master coroutine ends for any reason or is paused or resumed /// that will also happen to the slave coroutine. If a two way link is desired then just call this funciton twice with /// parameters reversed. /// /// The coroutine that generates the link events /// The coroutine that recieves the link events /// The number of coroutines that were linked. public static int LinkCoroutines(CoroutineHandle master, CoroutineHandle slave) { if (!IsRunning(slave) || !master.IsValid) { return 0; } else if (!IsRunning(master)) { KillCoroutines(slave); return 1; } if (Links.ContainsKey(master)) { if (!Links[master].Contains(slave)) { Links[master].Add(slave); return 1; } else { return 0; } } else { Links.Add(master, new HashSet { slave }); return 1; } } /// /// This will remove the link between two coroutine handles if one exists. /// /// The coroutine that generates the link events /// The coroutine that recieves the link events /// if true this will also remove any links that exist from the slave to the master /// The number of coroutines that were unlinked. public static int UnlinkCoroutines(CoroutineHandle master, CoroutineHandle slave, bool twoWay = false) { int count = 0; if (Links.ContainsKey(master) && Links[master].Contains(slave)) { if (Links[master].Count <= 1) Links.Remove(master); else Links[master].Remove(slave); count++; } if (twoWay && Links.ContainsKey(slave) && Links[slave].Contains(master)) { if (Links[slave].Count <= 1) Links.Remove(slave); else Links[slave].Remove(master); count++; } return count; } /// /// You can use something like "yield return Timing.GetMyHandle(x => myHandle = x)" inside a running coroutine to get /// that coroutine's handle. /// /// The function that will recieve the handle. public static float GetMyHandle(System.Action reciever) { _tmpRef = reciever; ReplacementFunction = GetHandleHelper; return float.NaN; } private static IEnumerator GetHandleHelper(IEnumerator input, CoroutineHandle handle) { System.Action reciever = _tmpRef as System.Action; if (reciever != null) reciever(handle); return input; } /// /// Use the command "yield return Timing.SwitchCoroutine(segment);" to switch this coroutine to /// the given segment on the default instance. /// /// The new segment to run in. public static float SwitchCoroutine(Segment newSegment) { _tmpSegment = newSegment; ReplacementFunction = SwitchCoroutineRepS; return float.NaN; } private static IEnumerator SwitchCoroutineRepS(IEnumerator coptr, CoroutineHandle handle) { Timing instance = GetInstance(handle.Key); instance.RunCoroutineInternal(coptr, _tmpSegment, instance._processLayers.ContainsKey(handle) ? instance._processLayers[handle] : (int?)null, instance._processTags.ContainsKey(handle) ? instance._processTags[handle] : null, handle, false); return null; } /// /// Use the command "yield return Timing.SwitchCoroutine(segment, tag);" to switch this coroutine to /// the given values. /// /// The new segment to run in. /// The new tag to apply, or null to remove this coroutine's tag. public static float SwitchCoroutine(Segment newSegment, string newTag) { _tmpSegment = newSegment; _tmpRef = newTag; ReplacementFunction = SwitchCoroutineRepST; return float.NaN; } private static IEnumerator SwitchCoroutineRepST(IEnumerator coptr, CoroutineHandle handle) { Timing instance = GetInstance(handle.Key); instance.RunCoroutineInternal(coptr, _tmpSegment, instance._processLayers.ContainsKey(handle) ? instance._processLayers[handle] : (int?)null, _tmpRef as string, handle, false); return null; } /// /// Use the command "yield return Timing.SwitchCoroutine(segment, layer);" to switch this coroutine to /// the given values. /// /// The new segment to run in. /// The new layer to apply. public static float SwitchCoroutine(Segment newSegment, int newLayer) { _tmpSegment = newSegment; _tmpInt = newLayer; ReplacementFunction = SwitchCoroutineRepSL; return float.NaN; } private static IEnumerator SwitchCoroutineRepSL(IEnumerator coptr, CoroutineHandle handle) { Timing instance = GetInstance(handle.Key); instance.RunCoroutineInternal(coptr, _tmpSegment, _tmpInt, instance._processTags.ContainsKey(handle) ? instance._processTags[handle] : null, handle, false); return null; } /// /// Use the command "yield return Timing.SwitchCoroutine(segment, layer, tag);" to switch this coroutine to /// the given values. /// /// The new segment to run in. /// The new layer to apply. /// The new tag to apply, or null to remove this coroutine's tag. public static float SwitchCoroutine(Segment newSegment, int newLayer, string newTag) { _tmpSegment = newSegment; _tmpInt = newLayer; _tmpRef = newTag; ReplacementFunction = SwitchCoroutineRepSLT; return float.NaN; } private static IEnumerator SwitchCoroutineRepSLT(IEnumerator coptr, CoroutineHandle handle) { GetInstance(handle.Key).RunCoroutineInternal(coptr, _tmpSegment, _tmpInt, _tmpRef as string, handle, false); return null; } /// /// Use the command "yield return Timing.SwitchCoroutine(tag);" to switch this coroutine to /// the given tag. /// /// The new tag to apply, or null to remove this coroutine's tag. public static float SwitchCoroutine(string newTag) { _tmpRef = newTag; ReplacementFunction = SwitchCoroutineRepT; return float.NaN; } private static IEnumerator SwitchCoroutineRepT(IEnumerator coptr, CoroutineHandle handle) { Timing instance = GetInstance(handle.Key); instance.RemoveTagOnInstance(handle); if ((_tmpRef as string) != null) instance.AddTagOnInstance((string)_tmpRef, handle); return coptr; } /// /// Use the command "yield return Timing.SwitchCoroutine(layer);" to switch this coroutine to /// the given layer. /// /// The new layer to apply. public static float SwitchCoroutine(int newLayer) { _tmpInt = newLayer; ReplacementFunction = SwitchCoroutineRepL; return float.NaN; } private static IEnumerator SwitchCoroutineRepL(IEnumerator coptr, CoroutineHandle handle) { RemoveLayer(handle); GetInstance(handle.Key).AddLayerOnInstance(_tmpInt, handle); return coptr; } /// /// Use the command "yield return Timing.SwitchCoroutine(layer, tag);" to switch this coroutine to /// the given tag. /// /// The new layer to apply. /// The new tag to apply, or null to remove this coroutine's tag. public static float SwitchCoroutine(int newLayer, string newTag) { _tmpInt = newLayer; _tmpRef = newTag; ReplacementFunction = SwitchCoroutineRepLT; return float.NaN; } private static IEnumerator SwitchCoroutineRepLT(IEnumerator coptr, CoroutineHandle handle) { Timing instance = GetInstance(handle.Key); instance.RemoveLayerOnInstance(handle); instance.AddLayerOnInstance(_tmpInt, handle); instance.RemoveTagOnInstance(handle); if ((_tmpRef as string) != null) instance.AddTagOnInstance((string)_tmpRef, handle); return coptr; } /// /// Calls the specified action after a specified number of seconds. /// /// The number of seconds to wait before calling the action. /// The action to call. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallDelayed(float delay, System.Action action) { return action == null ? new CoroutineHandle() : RunCoroutine(Instance._DelayedCall(delay, action, null)); } /// /// Calls the specified action after a specified number of seconds. /// /// The number of seconds to wait before calling the action. /// The action to call. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallDelayedOnInstance(float delay, System.Action action) { return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_DelayedCall(delay, action, null)); } /// /// Calls the specified action after a specified number of seconds. /// /// The number of seconds to wait before calling the action. /// The action to call. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed /// before calling the action. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallDelayed(float delay, System.Action action, GameObject gameObject) { return action == null ? new CoroutineHandle() : RunCoroutine(Instance._DelayedCall(delay, action, gameObject), gameObject); } /// /// Calls the specified action after a specified number of seconds. /// /// The number of seconds to wait before calling the action. /// The action to call. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed /// before calling the action. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallDelayedOnInstance(float delay, System.Action action, GameObject gameObject) { return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_DelayedCall(delay, action, gameObject), gameObject); } private IEnumerator _DelayedCall(float delay, System.Action action, GameObject cancelWith) { yield return WaitForSecondsOnInstance(delay); if (ReferenceEquals(cancelWith, null) || cancelWith != null) action(); } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(period, action, null)); if (!float.IsPositiveInfinity(timeframe)) RunCoroutine(Instance._WatchCall(timeframe, handle, null, onDone)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(period, action, null)); if (!float.IsPositiveInfinity(timeframe)) RunCoroutineOnInstance(_WatchCall(timeframe, handle, null, onDone)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(period, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(period, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(period, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) RunCoroutine(Instance._WatchCall(timeframe, handle, null, onDone), timing); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(period, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(timeframe, handle, null, onDone), timing)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(period, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(period, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(float timeframe, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(0f, action, null)); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(timeframe, handle, null, onDone))); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(0f, action, null)); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(timeframe, handle, null, onDone))); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(float timeframe, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(0f, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(0f, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(float timeframe, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(0f, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(timeframe, handle, null, onDone), timing)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(0f, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(timeframe, handle, null, onDone), timing)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(float timeframe, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(0f, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(0f, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } private IEnumerator _WatchCall(float timeframe, CoroutineHandle handle, GameObject gObject, System.Action onDone) { yield return WaitForSecondsOnInstance(timeframe); KillCoroutinesOnInstance(handle); if (onDone != null && (ReferenceEquals(gObject, null) || gObject != null)) onDone(); } private IEnumerator _CallContinuously(float period, System.Action action, GameObject gObject) { while (ReferenceEquals(gObject, null) || gObject != null) { yield return WaitForSecondsOnInstance(period); if (ReferenceEquals(gObject, null) || (gObject != null && gObject.activeInHierarchy)) action(); } } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(T reference, float timeframe, float period, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, period, action, null)); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, null, onDone))); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(T reference, float timeframe, float period, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, period, action, null)); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, null, onDone))); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(T reference, float timeframe, float period, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, period, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(T reference, float timeframe, float period, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, period, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(T reference, float timeframe, float period, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, period, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, null, onDone), timing)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(T reference, float timeframe, float period, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, period, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, null, onDone), timing)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallPeriodically(T reference, float timeframe, float period, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, period, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } /// /// Calls the supplied action at the given rate for a given number of seconds. /// /// A value that will be passed in to the supplied action each period. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The amount of time between calls. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallPeriodicallyOnInstance(T reference, float timeframe, float period, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, period, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(T reference, float timeframe, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, 0f, action, null)); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, null, onDone))); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(T reference, float timeframe, System.Action action, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, 0f, action, null)); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, null, onDone))); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(T reference, float timeframe, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, 0f, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(T reference, float timeframe, System.Action action, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, 0f, action, gameObject), gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, gameObject, onDone), gameObject)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(T reference, float timeframe, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, 0f, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, null, onDone), timing)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(T reference, float timeframe, System.Action action, Segment timing, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, 0f, action, null), timing); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, null, onDone), timing)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public static CoroutineHandle CallContinuously(T reference, float timeframe, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, 0f, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutine(Instance._WatchCall(reference, timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } /// /// Calls the supplied action every frame for a given number of seconds. /// /// A value that will be passed in to the supplied action each frame. /// The number of seconds that this function should run. Use float.PositiveInfinity to run indefinitely. /// The action to call every frame. /// The timing segment to run in. /// A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed or disabled /// before calling the action. /// An optional action to call when this function finishes. /// The handle to the coroutine that is started by this function. public CoroutineHandle CallContinuouslyOnInstance(T reference, float timeframe, System.Action action, Segment timing, GameObject gameObject, System.Action onDone = null) { CoroutineHandle handle = action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, 0f, action, gameObject), timing, gameObject); if (!float.IsPositiveInfinity(timeframe)) LinkCoroutines(handle, RunCoroutineOnInstance(_WatchCall(reference, timeframe, handle, gameObject, onDone), timing, gameObject)); return handle; } private IEnumerator _WatchCall(T reference, float timeframe, CoroutineHandle handle, GameObject gObject, System.Action onDone) { yield return WaitForSecondsOnInstance(timeframe); KillCoroutinesOnInstance(handle); if (onDone != null && (ReferenceEquals(gObject, null) || gObject != null)) onDone(reference); } private IEnumerator _CallContinuously(T reference, float period, System.Action action, GameObject gObject) { while ((ReferenceEquals(gObject, null) || gObject != null)) { yield return WaitForSecondsOnInstance(period); if (ReferenceEquals(gObject, null) || (gObject != null && gObject.activeInHierarchy)) action(reference); } } private struct ProcessIndex : System.IEquatable { public Segment seg; public int i; public bool Equals(ProcessIndex other) { return seg == other.seg && i == other.i; } public override bool Equals(object other) { if (other is ProcessIndex) return Equals((ProcessIndex)other); return false; } public static bool operator ==(ProcessIndex a, ProcessIndex b) { return a.seg == b.seg && a.i == b.i; } public static bool operator !=(ProcessIndex a, ProcessIndex b) { return a.seg != b.seg || a.i != b.i; } public override int GetHashCode() { return (((int)seg - 4) * (int.MaxValue / 7)) + i; } } [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] public new Coroutine StartCoroutine(System.Collections.IEnumerator routine) { return null; } [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] public new Coroutine StartCoroutine(string methodName, object value) { return null; } [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] public new Coroutine StartCoroutine(string methodName) { return null; } [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] public new Coroutine StartCoroutine_Auto(System.Collections.IEnumerator routine) { return null; } [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] public new void StopCoroutine(string methodName) {} [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] public new void StopCoroutine(System.Collections.IEnumerator routine) {} [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] public new void StopCoroutine(Coroutine routine) {} [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] public new void StopAllCoroutines() {} [System.Obsolete("Use your own GameObject for this.", true)] public new static void Destroy(Object obj) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void Destroy(Object obj, float f) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void DestroyObject(Object obj) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void DestroyObject(Object obj, float f) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void DestroyImmediate(Object obj) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void DestroyImmediate(Object obj, bool b) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void Instantiate(Object obj) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void Instantiate(Object original, Vector3 position, Quaternion rotation) { } [System.Obsolete("Use your own GameObject for this.", true)] public new static void Instantiate(T original) where T : Object { } [System.Obsolete("Just.. no.", true)] public new static T FindObjectOfType() where T : Object { return null; } [System.Obsolete("Just.. no.", true)] public new static Object FindObjectOfType(System.Type t) { return null; } [System.Obsolete("Just.. no.", true)] public new static T[] FindObjectsOfType() where T : Object { return null; } [System.Obsolete("Just.. no.", true)] public new static Object[] FindObjectsOfType(System.Type t) { return null; } [System.Obsolete("Just.. no.", true)] public new static void print(object message) { } } /// /// The timing segment that a coroutine is running in or should be run in. /// public enum Segment { /// /// Sometimes returned as an error state /// Invalid = -1, /// /// This is the default timing segment /// Update, /// /// This is primarily used for physics calculations /// FixedUpdate, /// /// This is run immediately after update /// LateUpdate, /// /// This executes, by default, about as quickly as the eye can detect changes in a text field /// SlowUpdate, /// /// This is the same as update, but it ignores Unity's timescale /// RealtimeUpdate, /// /// This is a coroutine that runs in the unity editor while your app is not in play mode /// EditorUpdate, /// /// This executes in the unity editor about as quickly as the eye can detect changes in a text field /// EditorSlowUpdate, /// /// This segment executes as the very last action before the frame is done /// EndOfFrame, /// /// This segment can be configured to execute and/or define its notion of time in custom ways /// ManualTimeframe } /// /// How much debug info should be sent to the Unity profiler. NOTE: Setting this to anything above none shows up in the profiler as a /// decrease in performance and a memory alloc. Those effects do not translate onto device. /// public enum DebugInfoType { /// /// None coroutines will be separated in the Unity profiler /// None, /// /// The Unity profiler will identify each coroutine individually /// SeperateCoroutines, /// /// Coroutines will be separated and any tags or layers will be identified /// SeperateTags } /// /// How the new coroutine should act if there are any existing coroutines running. /// public enum SingletonBehavior { /// /// Don't run this corutine if there are any matches /// Abort, /// /// Kill any matching coroutines when this one runs /// Overwrite, /// /// Pause this coroutine until any matches finish running /// Wait } /// /// A handle for a MEC coroutine. /// public struct CoroutineHandle : System.IEquatable { private const byte ReservedSpace = 0x0F; private readonly static int[] NextIndex = { ReservedSpace + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private readonly int _id; public byte Key { get { return (byte)(_id & ReservedSpace); } } public CoroutineHandle(byte ind) { if (ind > ReservedSpace) ind -= ReservedSpace; _id = NextIndex[ind] + ind; NextIndex[ind] += ReservedSpace + 1; } public bool Equals(CoroutineHandle other) { return _id == other._id; } public override bool Equals(object other) { if (other is CoroutineHandle) return Equals((CoroutineHandle)other); return false; } public static bool operator ==(CoroutineHandle a, CoroutineHandle b) { return a._id == b._id; } public static bool operator !=(CoroutineHandle a, CoroutineHandle b) { return a._id != b._id; } public override int GetHashCode() { return _id; } public override string ToString() { if (Timing.GetTag(this) == null) { if (Timing.GetLayer(this) == null) return Timing.GetDebugName(this); else return Timing.GetDebugName(this) + " Layer: " + Timing.GetLayer(this); } else { if (Timing.GetLayer(this) == null) return Timing.GetDebugName(this) + " Tag: " + Timing.GetTag(this); else return Timing.GetDebugName(this) + " Tag: " + Timing.GetTag(this) + " Layer: " + Timing.GetLayer(this); } } /// /// Get or set the corrosponding coroutine's tag. Null removes the tag or represents no tag assigned. /// public string Tag { get { return Timing.GetTag(this); } set { Timing.SetTag(this, value); } } /// /// Get or set the corrosponding coroutine's layer. Null removes the layer or represents no layer assigned. /// public int? Layer { get { return Timing.GetLayer(this); } set { if (value == null) Timing.RemoveLayer(this); else Timing.SetLayer(this, (int)value); } } /// /// Get or set the coorsponding coroutine's segment. /// public Segment Segment { get { return Timing.GetSegment(this); } set { Timing.SetSegment(this, value); } } /// /// Is true until the coroutine function ends or is killed. Setting this to false will kill the coroutine. /// public bool IsRunning { get { return Timing.IsRunning(this); } set { if(!value) Timing.KillCoroutines(this); } } /// /// Is true while the coroutine is paused. /// NOTE: This value was inverted. Replaced with IsAliveAndPaused which is not inverted. /// [System.Obsolete("This value was inverted. Replaced with IsAliveAndPaused which is not inverted.", false)] public bool IsPaused { get { return Timing.IsPaused(this); } set { if (value) Timing.PauseCoroutines(this); else Timing.ResumeCoroutines(this);} } /// /// Is true while the coroutine is paused. Setting this value will pause or resume the coroutine. /// public bool IsAliveAndPaused { get { return Timing.IsAliveAndPaused(this); } set { if (value) Timing.PauseCoroutines(this); else Timing.ResumeCoroutines(this); } } /// /// Is true if this handle may have been a valid handle at some point. (i.e. is not an uninitialized handle, error handle, or a key to a coroutine lock) /// public bool IsValid { get { return Key != 0; } } } } public static class MECExtensionMethods { /// /// Adds a delay to the beginning of this coroutine. /// /// The coroutine handle to act upon. /// The number of seconds to delay this coroutine. /// The modified coroutine handle. public static IEnumerator Delay(this IEnumerator coroutine, float timeToDelay) { yield return MEC.Timing.WaitForSeconds(timeToDelay); while (coroutine.MoveNext()) yield return coroutine.Current; } /// /// Adds a delay to the beginning of this coroutine until a function returns true. /// /// The coroutine handle to act upon. /// The coroutine will be paused until this function returns true. /// The modified coroutine handle. public static IEnumerator Delay(this IEnumerator coroutine, System.Func condition) { while (!condition()) yield return 0f; while (coroutine.MoveNext()) yield return coroutine.Current; } /// /// Adds a delay to the beginning of this coroutine until a function returns true. /// /// The coroutine handle to act upon. /// A variable that will be passed into the condition function each time it is tested. /// The coroutine will be paused until this function returns true. /// The modified coroutine handle. public static IEnumerator Delay(this IEnumerator coroutine, T data, System.Func condition) { while (!condition(data)) yield return 0f; while (coroutine.MoveNext()) yield return coroutine.Current; } /// /// Adds a delay to the beginning of this coroutine in frames. /// /// The coroutine handle to act upon. /// The number of frames to delay this coroutine. /// The modified coroutine handle. public static IEnumerator DelayFrames(this IEnumerator coroutine, int framesToDelay) { while(framesToDelay-- > 0) yield return 0f; while (coroutine.MoveNext()) yield return coroutine.Current; } /// /// Cancels this coroutine when the supplied game object is destroyed or made inactive. /// /// The coroutine handle to act upon. /// The GameObject to test. /// The modified coroutine handle. public static IEnumerator CancelWith(this IEnumerator coroutine, GameObject gameObject) { while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (gameObject && gameObject.activeInHierarchy && coroutine.MoveNext())) yield return coroutine.Current; } /// /// Cancels this coroutine when either of the supplied game objects are destroyed or made inactive. /// /// The coroutine handle to act upon. /// The first GameObject to test. /// The second GameObject to test /// The modified coroutine handle. public static IEnumerator CancelWith(this IEnumerator coroutine, GameObject gameObject1, GameObject gameObject2) { while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (gameObject1 && gameObject1.activeInHierarchy && gameObject2 && gameObject2.activeInHierarchy && coroutine.MoveNext())) yield return coroutine.Current; } /// /// Cancels this coroutine when the supplied monobehavior is removed from its game object, or the game object is made inactive or destroyed. /// /// The coroutine handle to act upon. /// The GameObject to test. /// The modified coroutine handle. public static IEnumerator CancelWith(this IEnumerator coroutine, T script) where T : MonoBehaviour { GameObject myGO = script.gameObject; while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (myGO && myGO.activeInHierarchy && script != null && coroutine.MoveNext())) yield return coroutine.Current; } /// /// Cancels this coroutine when the supplied function returns false. /// /// The coroutine handle to act upon. /// The test function. True for continue, false to stop. /// The modified coroutine handle. public static IEnumerator CancelWith(this IEnumerator coroutine, System.Func condition) { if (condition == null) yield break; while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (condition() && coroutine.MoveNext())) yield return coroutine.Current; } /// /// Cancels this coroutine when the supplied game object is destroyed, but only pauses it while it's inactive. /// /// The coroutine handle to act upon. /// The GameObject to test. /// The modified coroutine handle. public static IEnumerator PauseWith(this IEnumerator coroutine, GameObject gameObject) { while(MEC.Timing.MainThread == System.Threading.Thread.CurrentThread && gameObject) { if (gameObject.activeInHierarchy) { if (coroutine.MoveNext()) yield return coroutine.Current; else yield break; } else { yield return MEC.Timing.WaitForOneFrame; } } } /// /// Cancels this coroutine when either of the supplied game objects are destroyed, but only pauses them while they're inactive. /// /// The coroutine handle to act upon. /// The first GameObject to test. /// The second GameObject to test /// The modified coroutine handle. public static IEnumerator PauseWith(this IEnumerator coroutine, GameObject gameObject1, GameObject gameObject2) { while (MEC.Timing.MainThread == System.Threading.Thread.CurrentThread && gameObject1 && gameObject2) { if (gameObject1.activeInHierarchy && gameObject2.activeInHierarchy) { if (coroutine.MoveNext()) yield return coroutine.Current; else yield break; } else { yield return MEC.Timing.WaitForOneFrame; } } } /// /// Cancels this coroutine when the supplied monobehavior is removed from its game object, or the game object is destroyed. Pauses the coroutine /// if the game object or script is disabled. /// /// The coroutine handle to act upon. /// The GameObject to test. /// The modified coroutine handle. public static IEnumerator PauseWith(this IEnumerator coroutine, T script) where T : MonoBehaviour { GameObject myGO = script.gameObject; while (MEC.Timing.MainThread == System.Threading.Thread.CurrentThread && myGO && myGO.GetComponent() != null) { if (myGO.activeInHierarchy && script.enabled) { if (coroutine.MoveNext()) yield return coroutine.Current; else yield break; } else { yield return MEC.Timing.WaitForOneFrame; } } } /// /// Pauses this coroutine whenever the supplied function returns false. /// /// The coroutine handle to act upon. /// The test function. True for continue, false to stop. /// The modified coroutine handle. public static IEnumerator PauseWith(this IEnumerator coroutine, System.Func condition) { if (condition == null) yield break; while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (condition() && coroutine.MoveNext())) yield return coroutine.Current; } /// /// Runs the supplied coroutine immediately after this one. /// /// The coroutine handle to act upon. /// The coroutine to run next. /// The modified coroutine handle. public static IEnumerator Append(this IEnumerator coroutine, IEnumerator nextCoroutine) { while (coroutine.MoveNext()) yield return coroutine.Current; if (nextCoroutine == null) yield break; while (nextCoroutine.MoveNext()) yield return nextCoroutine.Current; } /// /// Runs the supplied function immediately after this coroutine finishes. /// /// The coroutine handle to act upon. /// The action to run after this coroutine finishes. /// The modified coroutine handle. public static IEnumerator Append(this IEnumerator coroutine, System.Action onDone) { while (coroutine.MoveNext()) yield return coroutine.Current; if (onDone != null) onDone(); } /// /// Runs the supplied coroutine immediately before this one. /// /// The coroutine handle to act upon. /// The coroutine to run first. /// The modified coroutine handle. public static IEnumerator Prepend(this IEnumerator coroutine, IEnumerator lastCoroutine) { if (lastCoroutine != null) while (lastCoroutine.MoveNext()) yield return lastCoroutine.Current; while (coroutine.MoveNext()) yield return coroutine.Current; } /// /// Runs the supplied function immediately before this coroutine starts. /// /// The coroutine handle to act upon. /// The action to run before this coroutine starts. /// The modified coroutine handle. public static IEnumerator Prepend(this IEnumerator coroutine, System.Action onStart) { if (onStart != null) onStart(); while (coroutine.MoveNext()) yield return coroutine.Current; } /// /// Combines the this coroutine with another and runs them in a combined handle. /// /// The coroutine handle to act upon. /// The coroutine handle to combine. /// The modified coroutine handle. public static IEnumerator Superimpose(this IEnumerator coroutineA, IEnumerator coroutineB) { return Superimpose(coroutineA, coroutineB, MEC.Timing.Instance); } /// /// Combines the this coroutine with another and runs them in a combined handle. /// /// The coroutine handle to act upon. /// The coroutine handle to combine. /// The timing instance that this will be run in, if not the default instance. /// The modified coroutine handle. public static IEnumerator Superimpose(this IEnumerator coroutineA, IEnumerator coroutineB, MEC.Timing instance) { while (coroutineA != null || coroutineB != null) { if (coroutineA != null && !(instance.localTime < coroutineA.Current) && !coroutineA.MoveNext()) coroutineA = null; if (coroutineB != null && !(instance.localTime < coroutineB.Current) && !coroutineB.MoveNext()) coroutineB = null; if ((coroutineA != null && float.IsNaN(coroutineA.Current)) || (coroutineB != null && float.IsNaN(coroutineB.Current))) yield return float.NaN; else if (coroutineA != null && coroutineB != null) yield return coroutineA.Current < coroutineB.Current ? coroutineA.Current : coroutineB.Current; else if (coroutineA == null && coroutineB != null) yield return coroutineB.Current; else if (coroutineA != null) yield return coroutineA.Current; } } /// /// Uses the passed in function to change the return values of this coroutine. /// /// The coroutine handle to act upon. /// A function that takes the current return value and returns the new return. /// The modified coroutine handle. public static IEnumerator Hijack(this IEnumerator coroutine, System.Func newReturn) { if (newReturn == null) yield break; while (coroutine.MoveNext()) yield return newReturn(coroutine.Current); } }