Last active
October 29, 2019 16:42
-
-
Save drZool/7f37aa2dec2b03bb6f135a9dbdb4f8a4 to your computer and use it in GitHub Desktop.
Delay actions. Keeps all actions in a sorted list to minimize overhead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using UnityEngine; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using System; | |
| /// <summary> | |
| /// | |
| /// Delay actions. Keeps all actions in a sorted list to minimize overhead. | |
| /// | |
| /// </summary> | |
| /// <usage> | |
| /// | |
| /// DelayedAction.time.Delay( 0.2f, MyAction ); | |
| /// DelayedAction.unscaledTime.Delay( 0.3f, () => Debug.Log("Hello!") ); | |
| /// DelayedAction.frames.Delay( 1, () => Debug.Log("We waited one frame!") ); | |
| /// | |
| /// DelayedAction.time.At( 13.37f, WinGame ); | |
| /// </usage> | |
| public class DelayedAction : MonoBehaviour | |
| { | |
| static GameObject CreateGhost (string name) | |
| { | |
| var go = new GameObject (name); | |
| GameObject.DontDestroyOnLoad (go); | |
| go.hideFlags = HideFlags.HideAndDontSave; | |
| return go; | |
| } | |
| static DelayedActionTime _time; | |
| public static DelayedActionTime time { | |
| get { | |
| if (_time == null) | |
| _time = CreateGhost ("DelayedAction").AddComponent<DelayedActionTime> (); | |
| return _time; | |
| } | |
| } | |
| static DelayedActionUnscaledTime _unscaledTime; | |
| public static DelayedActionUnscaledTime unscaledTime { | |
| get { | |
| if (_unscaledTime == null) | |
| _unscaledTime = CreateGhost ("DelayedActionUnscaledTime").AddComponent<DelayedActionUnscaledTime> (); | |
| return _unscaledTime; | |
| } | |
| } | |
| static DelayedActionRealtimeSinceStartup _realtimeSinceStartup; | |
| public static DelayedActionRealtimeSinceStartup realtimeSinceStartup { | |
| get { | |
| if (_realtimeSinceStartup == null) | |
| _realtimeSinceStartup = CreateGhost ("DelayedActionRealtimeSinceStartup").AddComponent<DelayedActionRealtimeSinceStartup> (); | |
| return _realtimeSinceStartup; | |
| } | |
| } | |
| static DelayedActionFrames _frames; | |
| public static DelayedActionFrames frames { | |
| get { | |
| if (_frames == null) | |
| _frames = CreateGhost ("DelayedActionFrames").AddComponent<DelayedActionFrames> (); | |
| return _frames; | |
| } | |
| } | |
| /// <summary> | |
| /// Creates the using custom time. You are responsible for this object! it does not go away by itself | |
| /// </summary> | |
| /// <returns>The using custom time.</returns> | |
| /// <param name="timeProvider">Time provider.</param> | |
| public static DelayedActionCustomTime CreateUsingCustomTime (Func <float> timeProvider) | |
| { | |
| var component = CreateGhost ("DelayedActionCustomTime").AddComponent<DelayedActionCustomTime> (); | |
| component.timeProvider = timeProvider; | |
| return component; | |
| } | |
| } | |
| public struct TimedAction | |
| { | |
| public Action action; | |
| public float time; | |
| public int id; | |
| public TimedAction (float time, Action action, int id) | |
| { | |
| this.time = time; | |
| this.action = action; | |
| this.id = id; | |
| } | |
| } | |
| public class DelayedActionRealtimeSinceStartup : DelayedActionBase | |
| { | |
| public float now{ get { return Time.realtimeSinceStartup; } } | |
| void Update () | |
| { | |
| while (sortedActions.Count > 0 && sortedActions [0].time < Time.realtimeSinceStartup) { | |
| var a = sortedActions [0]; | |
| sortedActions.RemoveAt (0); | |
| a.action.Invoke (); | |
| } | |
| } | |
| public int Delay (float delay, Action action) | |
| { | |
| return At (Time.realtimeSinceStartup + delay, action); | |
| } | |
| public override int At (float time, Action action) | |
| { | |
| if (time <= Time.realtimeSinceStartup) { //Execute directly | |
| action.Invoke (); | |
| uniqueId++; //Had to return a valid id even if not added to sortedActions | |
| return uniqueId - 1; | |
| } | |
| return base.At (time, action); | |
| } | |
| } | |
| public class DelayedActionTime : DelayedActionBase | |
| { | |
| public float now{ get { return Time.time; } } | |
| void Update () | |
| { | |
| while (sortedActions.Count > 0 && sortedActions [0].time < Time.time) { | |
| var a = sortedActions [0]; | |
| sortedActions.RemoveAt (0); | |
| a.action.Invoke (); | |
| } | |
| } | |
| public int Delay (float delay, Action action) | |
| { | |
| return At (Time.time + delay, action); | |
| } | |
| public override int At (float time, Action action) | |
| { | |
| if (time <= Time.time) { //Execute directly | |
| action.Invoke (); | |
| uniqueId++; //Had to return a valid id even if not added to sortedActions | |
| return uniqueId - 1; | |
| } | |
| return base.At (time, action); | |
| } | |
| } | |
| public class DelayedActionUnscaledTime : DelayedActionBase | |
| { | |
| public float now{ get { return Time.unscaledTime; } } | |
| void Update () | |
| { | |
| while (sortedActions.Count > 0 && sortedActions [0].time < Time.unscaledTime) { | |
| var a = sortedActions [0]; | |
| sortedActions.RemoveAt (0); | |
| a.action.Invoke (); | |
| } | |
| } | |
| public int Delay (float delay, Action action) | |
| { | |
| return At (Time.unscaledTime + delay, action); | |
| } | |
| public override int At (float time, Action action) | |
| { | |
| if (time <= Time.unscaledTime) { //Execute directly | |
| action.Invoke (); | |
| uniqueId++; //Had to return a valid id even if not added to sortedActions | |
| return uniqueId - 1; | |
| } | |
| return base.At (time, action); | |
| } | |
| } | |
| public class DelayedActionFrames : DelayedActionBase | |
| { | |
| public int now{ get { return Time.frameCount; } } | |
| void Update () | |
| { | |
| while (sortedActions.Count > 0 && (int)sortedActions [0].time <= Time.frameCount) { | |
| var a = sortedActions [0]; | |
| sortedActions.RemoveAt (0); | |
| a.action.Invoke (); | |
| } | |
| } | |
| public int Delay (int delay, Action action) | |
| { | |
| return At (Time.frameCount + delay, action); | |
| } | |
| public override int At (float time, Action action) | |
| { | |
| return At ((int)time, action); | |
| } | |
| public int At (int frame, Action action) | |
| { | |
| if ((int)frame <= Time.frameCount) { //Execute directly | |
| action.Invoke (); | |
| uniqueId++; //Had to return a valid id even if not added to sortedActions | |
| return uniqueId - 1; | |
| } | |
| return base.At (frame, action); | |
| } | |
| } | |
| public class DelayedActionBase : MonoBehaviour | |
| { | |
| internal int uniqueId = int.MinValue; | |
| internal List<TimedAction> sortedActions = new List<TimedAction> (32); | |
| internal float lastTime { get; private set; } | |
| public virtual int At (float time, Action action) | |
| { | |
| if (time < lastTime) { //Optimization, lastTime can be removed | |
| for (int i = 0; i < sortedActions.Count; ++i) { | |
| if (sortedActions [i].time > time) { | |
| sortedActions.Insert (i, new TimedAction (time, action, uniqueId++)); | |
| return uniqueId - 1; | |
| } | |
| } | |
| } | |
| lastTime = time; | |
| sortedActions.Add (new TimedAction (time, action, uniqueId++)); | |
| return uniqueId - 1; | |
| } | |
| public void Remove (int? id) | |
| { | |
| if (id.HasValue) { | |
| Remove (id.Value); | |
| } | |
| } | |
| public void Remove (int id) | |
| { | |
| for (int i = 0; i < sortedActions.Count; ++i) { | |
| if (sortedActions [i].id == id) { | |
| sortedActions.RemoveAt (i); | |
| return; | |
| } | |
| } | |
| } | |
| public void RemoveAll(){ | |
| sortedActions.Clear (); | |
| } | |
| } | |
| public class DelayedActionCustomTime : DelayedActionBase | |
| { | |
| static float stub () | |
| { | |
| return 0; | |
| } | |
| public Func<float> timeProvider = stub; | |
| //Set this to something useful at runtime. | |
| public float now{ get { return timeProvider (); } } | |
| void Update () | |
| { | |
| while (sortedActions.Count > 0 && sortedActions [0].time < timeProvider ()) { | |
| var a = sortedActions [0]; | |
| sortedActions.RemoveAt (0); | |
| a.action.Invoke (); | |
| } | |
| } | |
| public int Delay (float delay, Action action) | |
| { | |
| return At (timeProvider () + delay, action); | |
| } | |
| public override int At (float time, Action action) | |
| { | |
| if (time <= timeProvider ()) { //Execute directly | |
| action.Invoke (); | |
| uniqueId++; //Had to return a valid id even if not added to sortedActions | |
| return uniqueId - 1; | |
| } | |
| return base.At (time, action); | |
| } | |
| } | |
| public class DelayedTest : MonoBehaviour | |
| { | |
| void Start () | |
| { | |
| float now = Time.time; | |
| var t = DelayedAction.time; | |
| t.At (10f, () => LogTime ("G 10")); | |
| t.At (1f, () => LogTime ("C 1")); | |
| t.At (3f, () => LogTime ("F 3")); | |
| t.At (2f, () => LogTime ("E 2")); | |
| t.At (1.5f, () => LogTime ("D at time 115")); | |
| t.At (0.1f, () => LogTime ("B 0.1")); | |
| int id = t.Delay (0.0f, () => LogTime ("A unremovable 0")); | |
| t.Remove (id); | |
| id = t.Delay (0.0000001f, () => LogTime ("A removable 0.0000001")); | |
| t.Remove (id); | |
| t.At ((int)Time.time + 1, SpawnDelay); | |
| var u = DelayedAction.unscaledTime; | |
| // u.At (1, () => Time.timeScale = 0f); | |
| // u.At (1, () => LogTime ("--------- Timescale at " + Time.timeScale)); | |
| // u.At (2, () => LogTime ("Unscaled 2")); | |
| // u.At (5, () => Time.timeScale = 2f); | |
| // u.At (5, () => LogTime ("--------- Timescale at " + Time.timeScale)); | |
| // u.At (6, () => LogTime ("Unscaled 6")); | |
| var r = DelayedAction.realtimeSinceStartup; | |
| r.At (2, () => LogTime ("Realtime At 2")); | |
| r.At (r.now + 1, () => LogTime ("Realtime At >now< + 1")); | |
| var c = DelayedAction.CreateUsingCustomTime (GetMusicTime); | |
| c.Delay (1, () => LogTime ("Custom time 1")); | |
| t.Delay (0.1f, () => Heavy (3)); | |
| } | |
| void Heavy (int left) | |
| { | |
| var t = DelayedAction.time; | |
| if (left <= 0) { | |
| t.At (t.lastTime, Done); | |
| return; | |
| } | |
| LogTime ("Heavy add random delays " + countMax); | |
| for (int i = 0; i < countMax; ++i) { | |
| t.Delay (UnityEngine.Random.value * 5, Add); | |
| } | |
| t.Delay (2.5f, () => Heavy (left - 1)); | |
| } | |
| void Done () | |
| { | |
| LogTime ("Added " + count + "/" + countMax); | |
| } | |
| void Add () | |
| { | |
| count++; | |
| } | |
| int countMax = 1000; | |
| int count = 0; | |
| float GetMusicTime () | |
| { | |
| return 0; | |
| } | |
| void LogTime (string message = null) | |
| { | |
| Debug.LogFormat ("{3} | time {0:0.00} unscaled {1:0.00} realtime {2:0.00}", Time.time, Time.unscaledTime, Time.realtimeSinceStartup, message); | |
| } | |
| bool on; | |
| void SpawnDelay () | |
| { | |
| Debug.Log ("SpawnDelay " + Time.time); | |
| on = !on; | |
| if (on) { | |
| DelayedAction.time.At ((int)Time.time + 1, SpawnDelay); | |
| } else { | |
| DelayedAction.time.Delay (1, SpawnDelay); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment