Advertisement
mvaganov

Timer.cs

Oct 24th, 2017 (edited)
474
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 40.43 KB | None | 0 0
  1. using System.Collections.Generic;
  2. #if UNITY_5_3_OR_NEWER
  3. using System.Collections;
  4. using System.Text;
  5. using UnityEngine;
  6. #if UNITY_EDITOR
  7. using UnityEditor;
  8. using System.Linq;
  9. #endif
  10. #endif
  11.  
  12. /* // example code:
  13. Chrono.setTimeout (() => {
  14.     Chrono.Log("This will print 3 seconds after setTimeout was called!");
  15. }, 3000);
  16. // Code above will work like you expect if Chrono.Update() is being called regularly, which will happen if Unity has at least one Timer or MainTimer instance.
  17. */
  18.  
  19. // license: Copyfree, public domain. This is free code! Great artists, steal this code!
  20. // latest version at: https://pastebin.com/raw/h61nAC3E -- (2020/11/26)
  21. namespace NonStandard {
  22. #if UNITY_5_3_OR_NEWER
  23.     public class Timer : MonoBehaviour {
  24.         [Tooltip("When to trigger"), ContextMenuItem("Create Main Timer", "CraeteMainTimer")]
  25.         public float seconds = 1;
  26.         [Tooltip("Transform to teleport to\nSceneAsset to load a new scene\nAudioClip to play audio\nGameObject to SetActivate(true)")]
  27.         public ObjectPtr whatToActivate = new ObjectPtr();
  28.         [Tooltip("restart a timer after triggering")]
  29.         public bool repeat = false;
  30.         [Tooltip("attempt to deactivate instead of activate")]
  31.         public bool deactivate = false;
  32.         public int a;
  33.  
  34.         private void DoTimer() {
  35.             if (repeat) {
  36.                 Chrono.setTimeout(DoTimer, (long)(seconds * 1000));
  37.             }
  38.             Chrono.ScheduledTask todo = Chrono.setTimeout(whatToActivate.Data, (long)(seconds * 1000));
  39.             todo.who = this;
  40.             todo.activate = !deactivate;
  41.         }
  42.         private void Awake() { MainTimer.Instance(); }
  43.         void Start() { if (whatToActivate.Data != null) { DoTimer(); } }
  44.  
  45. #if UNITY_EDITOR
  46.         public void CreateMainTimer() {
  47.             MainTimer found = FindObjectOfType<MainTimer>();
  48.             if (found == null) {
  49.                 gameObject.AddComponent<MainTimer>();
  50.             } else {
  51.                 Debug.LogWarning("will not create another " + nameof(MainTimer) + ", one already at " + found.transform.HierarchyPath());
  52.             }
  53.         }
  54. #endif
  55.     }
  56.  
  57.     public static class TransformExtention {
  58.         public static string HierarchyPath(this Transform t) {
  59.             StringBuilder sb = new StringBuilder();
  60.             sb.Append(t.name);
  61.             t = t.parent;
  62.             while (t != null) {
  63.                 sb.Insert(0, t.name + "/");
  64.                 t = t.parent;
  65.             }
  66.             return sb.ToString();
  67.         }
  68.     }
  69.  
  70.     /// <summary>
  71.     /// a class that enables the timer queue to be observed in the editor at runtime
  72.     /// </summary>
  73.     public class MainTimer : MonoBehaviour {
  74.         public Chrono timerQueue;
  75.         [System.Serializable]
  76.         public class PauseEvents {
  77.             [Tooltip("do this when time is paused")] public UnityEngine.Events.UnityEvent onPause;
  78.             [Tooltip("do this when time is unpaused")] public UnityEngine.Events.UnityEvent onUnpause;
  79.         }
  80.         public PauseEvents pauseEvents;
  81.  
  82.         public void Awake() {
  83.             if (timerQueue != Chrono.Instance && timerQueue != null) { Chrono.Instance.Absorb(timerQueue); }
  84.             timerQueue = Chrono.Instance;
  85.             timerQueue.linkedToMainThread = System.Threading.Thread.CurrentThread;
  86.             timerQueue.Awake();
  87.             timerQueue.onPause += () => { pauseEvents.onPause.Invoke(); };
  88.             timerQueue.onUnpause += () => { pauseEvents.onUnpause.Invoke(); };
  89.         }
  90.         private void Start() { timerQueue.Start(); }
  91.         public void Update() { timerQueue.Update(); }
  92.         void Pause() { timerQueue.Pause(); }
  93.         void Unpause() { timerQueue.Unpause(); }
  94.         void OnApplicationPause(bool paused) { timerQueue.OnApplicationPause(paused); }
  95.         void OnDisable() { timerQueue.OnDisable(); }
  96.         void OnEnable() { timerQueue.OnEnable(); }
  97.         private void OnApplicationQuit() { timerQueue.OnApplicationQuit(); }
  98. #if UNITY_EDITOR
  99.         public void OnValidate() { if (timerQueue == null) { timerQueue = Chrono.Instance; } timerQueue.OnValidate(); }
  100. #endif
  101.  
  102.         internal static MainTimer s_instance;
  103.  
  104.         public static MainTimer Instance() {
  105.             if (s_instance != null) return s_instance;
  106.             s_instance = FindObjectOfType<MainTimer>();
  107.             if (s_instance == null) { // if it doesn't exist
  108.                 GameObject g = new GameObject("<" + typeof(Chrono).Name + ">");
  109.                 s_instance = g.AddComponent<MainTimer>(); // create one
  110.             }
  111.             return s_instance;
  112.         }
  113.     }
  114. #endif
  115.  
  116.     [System.Serializable]
  117.     public class Chrono {
  118.         // https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer?redirectedfrom=MSDN&view=netframework-4.8
  119.         // explicitly NOT using System.Timers.Timer because it's multi-threaded, and many Unity methods, notably the time keeping ones, must be called from the main thread.
  120.  
  121.         /// The singleton
  122.         private static Chrono s_instance = null;
  123.         /// [Tooltip("keeps track of how long each update takes. If a timer-update takes longer than this, stop executing events and do them later. Less than 0 for no limit, 0 for one event per update.")]
  124.         public int maxMillisecondsPerUpdate = 100;
  125.         private long maxTicksPerUpdate;
  126.         public long updateCount { get; private set; }
  127.         public static long UpdateCount => Instance.updateCount;
  128.         /// queue of things to do using game time. use this for in-game events that can be paused or slowed down with time dialation.
  129.         public List<ScheduledTask> queue = new List<ScheduledTask>();
  130.         /// queue of things to do using real-time game time. use this for UI events, or things that use the real-world as a reference, that should always work at the same rate
  131.         public List<ScheduledTask> queueRealtime = new List<ScheduledTask>();
  132.         /// While this is zero, use system time. As soon as time becomes perturbed, by pause or time scale, this now keeps track of game-time. To reset time back to realtime, use SynchToRealtime()
  133.         private long alternativeTicks = 0;
  134.         /// [Tooltip("stop advancing time & executing the queue?")]
  135.         public bool paused = false;
  136.         private bool pausedLastFrame = false;
  137.         internal System.Threading.Thread linkedToMainThread = null;
  138.         /// The timer counts in milliseconds, Unity measures in fractions of a second. This value reconciles fractional milliseconds.
  139.         private float leftOverTime = 0;
  140.         /// if actions are interrupted, probably by a deadline, this keeps track of what was being done
  141.         private List<ScheduledTask> _currentlyDoing = new List<ScheduledTask>();
  142.         private int currentlyDoneThingIndex = 0;
  143.  
  144.         public System.Action onPause, onUnpause;
  145.  
  146.         [System.Serializable]
  147.         public class ScheduledTask {
  148.             public string description;
  149.             /// Unix Epoch Time Milliseconds
  150.             public long when;
  151.             /// could be a delegate, or an executable object
  152.             public object what;
  153.             /// a parameter for 'who' wants this particular 'what' to be done, like a context.
  154.             public object who;
  155.             /// whether or not to DO or UN-do
  156.             public bool activate = true;
  157.             /// what could be a delegate, or an executable object, as executed by a Trigger
  158.             public ScheduledTask(long when, object what, string description = null, object who = null) {
  159.                 if (description == null) {
  160.                     if (what != null && typeof(System.Action).IsAssignableFrom(what.GetType())) {
  161.                         System.Action a = what as System.Action;
  162.                         description = a.Method.Name;
  163.                     } else if (what != null) {
  164.                         description = what.ToString();
  165.                     }
  166.                 }
  167.                 this.description = description; this.when = when; this.what = what; this.who = who;
  168.             }
  169.             /// comparer, used to sort into a list
  170.             public class Comparer : IComparer<ScheduledTask> {
  171.                 public int Compare(ScheduledTask x, ScheduledTask y) { return x.when.CompareTo(y.when); }
  172.             }
  173.             public static Comparer compare = new Comparer();
  174.         }
  175.  
  176.         public static void Log(string text, bool error = false) {
  177. #if UNITY_5_3_OR_NEWER
  178.             if (error) Debug.LogError(text);
  179.             else Debug.Log(text);
  180. #else
  181.             System.Console.WriteLine(text);
  182. #endif
  183.         }
  184.  
  185.         public static Chrono Instance => (s_instance != null) ? s_instance : s_instance = new Chrono();
  186.  
  187.         /// Unix Time: milliseconds since Jan 1 1970
  188.         public static long NowRealtime { get { return System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; } }
  189.         /// a time keeping mechanism that is very fast. no guarantees about the range of values, just that time is kept.
  190.         public static long NowRealTicks { get { return System.Environment.TickCount; } }
  191.  
  192.         /// game time right now (modified by pausing or Time.timeScale)
  193.         public long now { get { return (alternativeTicks == 0) ? NowRealtime : alternativeTicks; } }
  194.         public long nowTicks { get { return (alternativeTicks == 0) ? NowRealTicks : alternativeTicks; } }
  195.  
  196.         /// game time right now (modified by pausing or Time.timeScale)
  197.         public static long Now { get { return Instance.now; } }
  198.         public static long NowTicks { get { return Instance.nowTicks; } }
  199.  
  200.         private static bool _quitting = false;
  201.         public static bool Quitting { get { return _quitting; } internal set { _quitting = value; } }
  202.         public void OnApplicationQuit() { _quitting = true; }
  203.  
  204.         /// clears the difference between game time and real time
  205.         public void SyncToRealtime() { alternativeTicks = 0; }
  206.  
  207.         public void Absorb(Chrono otherTimerQueue) {
  208.             for (int i = 0; i < otherTimerQueue.queue.Count; ++i) {
  209.                 ScheduledTask todo = otherTimerQueue.queue[i];
  210.                 queue.Insert(BestIndexFor(todo.when, queue), todo);
  211.             }
  212.             otherTimerQueue.queue.Clear();
  213.             for (int i = 0; i < otherTimerQueue.queue.Count; ++i) {
  214.                 ScheduledTask todo = otherTimerQueue.queueRealtime[i];
  215.                 queueRealtime.Insert(BestIndexFor(todo.when, queueRealtime), todo);
  216.             }
  217.             otherTimerQueue.queueRealtime.Clear();
  218.         }
  219.  
  220.         private int BestIndexFor(long soon, List<ScheduledTask> a_queue) {
  221.             int index = 0;
  222.             if (a_queue.Count < 8) { // for small lists (which happen A LOT), linear search is fine.
  223.                 for (index = 0; index < a_queue.Count; ++index) {
  224.                     if (a_queue[index].when > soon) break;
  225.                 }
  226.             } else {
  227.                 ScheduledTask toInsert = new ScheduledTask(soon, null);
  228.                 index = a_queue.BinarySearch(toInsert, ScheduledTask.compare);
  229.                 if (index < 0) { index = ~index; }
  230.             }
  231.             return index;
  232.         }
  233.  
  234.         /// <summary>as the JavaScript function</summary>
  235.         /// <param name="action">Action. an object to trigger, expected to be a delegate or System.Action</param>
  236.         /// <param name="delayMilliseconds">Delay milliseconds.</param>
  237.         public ScheduledTask SetTimeout(System.Action action, long delayMilliseconds) {
  238.             return SetTimeout((object)action, delayMilliseconds);
  239.         }
  240.         /// <summary>as the JavaScript function</summary>
  241.         /// <param name="action">Action. an object to trigger, expected to be a delegate or System.Action</param>
  242.         /// <param name="delayMilliseconds">Delay milliseconds.</param>
  243.         public ScheduledTask SetTimeout(object action, long delayMilliseconds) {
  244.             long soon = nowTicks + delayMilliseconds;// * System.TimeSpan.TicksPerMillisecond;
  245.             ScheduledTask todo = new ScheduledTask(soon, action);
  246.             queue.Insert(BestIndexFor(soon, queue), todo);
  247.             LinkedToMainThreadCheck();
  248.             return todo;
  249.         }
  250.        
  251.         public List<ScheduledTask> UnsetTimeout(object action) {
  252.             List<ScheduledTask> unset = new List<ScheduledTask>();
  253.             for (int i = queue.Count-1; i >= 0; --i) {
  254.                 if (queue[i].what == action) {
  255.                     unset.Add(queue[i]);
  256.                     queue.RemoveAt(i);
  257.                 }
  258.             }
  259.             return unset;
  260.         }
  261.  
  262.         /// <summary>as the JavaScript function</summary>
  263.         /// <param name="action">Action. an object to trigger, expected to be a delegate or System.Action</param>
  264.         /// <param name="delayMilliseconds">Delay milliseconds.</param>
  265.         public ScheduledTask SetTimeoutRealtime(object action, long delayMilliseconds) {
  266.             long soon = NowRealTicks + delayMilliseconds;// * System.TimeSpan.TicksPerMillisecond;
  267.             ScheduledTask todo = new ScheduledTask(soon, action);
  268.             queueRealtime.Insert(BestIndexFor(soon, queueRealtime), todo);
  269.             LinkedToMainThreadCheck();
  270.             return todo;
  271.         }
  272.  
  273.         public void LinkedToMainThreadCheck() {
  274.             if (linkedToMainThread == null) {
  275.                 string extraMessage = "";
  276. #if UNITY_5_3_OR_NEWER
  277.                 extraMessage = "Create a " + nameof(Timer) + " or " + nameof(MainTimer) + " instance";
  278. #endif
  279.                 Log("timer queue given work without being bound to main thread. " + extraMessage, true);
  280.             }
  281.         }
  282.  
  283.         /// <param name="action">Action. what to do</param>
  284.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  285.         public static ScheduledTask setTimeout(object action, long delayMilliseconds) {
  286.             return Instance.SetTimeout(action, delayMilliseconds);
  287.         }
  288.  
  289.         public static List<ScheduledTask> unsetTimeout(System.Action action) {
  290.             return Instance.UnsetTimeout(action);
  291.         }
  292.        
  293.         /// <param name="action">Action. what to do</param>
  294.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  295.         public static ScheduledTask setTimeoutRealtime(object action, long delayMilliseconds) {
  296.             return Instance.SetTimeoutRealtime(action, delayMilliseconds);
  297.         }
  298.  
  299.         /// Allows implicit conversion of lambda expressions and delegates
  300.         /// <param name="action">Action. what to do</param>
  301.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  302.         public static ScheduledTask setTimeout(System.Action action, long delayMilliseconds) {
  303.             return Instance.SetTimeout(action, delayMilliseconds);
  304.         }
  305.  
  306.         /// <summary>Allows implicit conversion of lambda expressions and delegates</summary>
  307.         /// <param name="action">Action. what to do</param>
  308.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  309.         public static ScheduledTask setTimeoutRealtime(System.Action action, long delayMilliseconds) {
  310.             return Instance.SetTimeoutRealtime(action, delayMilliseconds);
  311.         }
  312.  
  313.         //void OnApplicationPause(bool paused) { if(alternativeTime == 0) { alternativeTime = now; } }
  314.         public void Pause() { paused = true; }
  315.         public void Unpause() { paused = false; }
  316.         public void OnApplicationPause(bool paused) { if (alternativeTicks == 0) { alternativeTicks = nowTicks; } }
  317.         public void OnDisable() { OnApplicationPause(true); }
  318.         public void OnEnable() { OnApplicationPause(false); }
  319.  
  320.         /// used to handle pause/unpause behavior
  321.         public delegate void BooleanAction(bool b);
  322.         public static void EquateUnityEditorPauseWithApplicationPause(BooleanAction b) {
  323. #if UNITY_EDITOR
  324.             // This method is run whenever the playmode state is changed.
  325.             UnityEditor.EditorApplication.pauseStateChanged += (UnityEditor.PauseState ps) => {
  326.                 b(ps == UnityEditor.PauseState.Paused);
  327.             };
  328. #endif
  329.         }
  330.  
  331.         protected void Init() { EquateUnityEditorPauseWithApplicationPause(OnApplicationPause); }
  332.  
  333.         public void Awake() {
  334.             if (s_instance != null && s_instance != this) { throw new System.Exception("There should only be one " + GetType()); }
  335.             s_instance = this;
  336.         }
  337.  
  338.         void RefreshTiming() { maxTicksPerUpdate = maxMillisecondsPerUpdate * System.TimeSpan.TicksPerMillisecond; }
  339.  
  340. #if UNITY_EDITOR
  341.         public void OnValidate() { RefreshTiming(); }
  342. #endif
  343.  
  344.         public void Start() { Init(); RefreshTiming(); }
  345.  
  346.         public void Update() {
  347.             updateCount++;
  348.             // handle pause behavior
  349.             if (paused) {
  350.                 if (!pausedLastFrame) {
  351.                     alternativeTicks = nowTicks;
  352.                     if (onPause != null) { onPause.Invoke(); }
  353.                     pausedLastFrame = true;
  354.                 }
  355.             } else if (pausedLastFrame) {
  356.                 if (onUnpause != null) { onUnpause.Invoke(); }
  357.                 pausedLastFrame = false;
  358.             }
  359.             // pump the timer queues, both realtime (takes priority) and game-time
  360.             long now_t, nowForReals = NowRealTicks;
  361.             long deadline = nowForReals + maxTicksPerUpdate;
  362.             int thingsDone = 0;
  363.             if (queueRealtime.Count > 0) {
  364.                 thingsDone = DoWhatIsNeededNow(queueRealtime, nowForReals, deadline);
  365.             }
  366.             if (queue.Count > 0 && !paused) {
  367.                 if (alternativeTicks == 0) {
  368.                     now_t = nowForReals;
  369. #if UNITY_5_3_OR_NEWER
  370.                     if (Time.timeScale != 1) { alternativeTicks = now_t; }
  371. #endif
  372.                 } else {
  373.                     float deltaTimeTicks =
  374. #if UNITY_5_3_OR_NEWER
  375.                     (Time.deltaTime * 1000);
  376. #else
  377.                     (__lastUpdate != 0) ? (NowRealTicks - __lastUpdate) : 0;
  378. #endif
  379.                     long deltaTimeTicksLong = (long)(deltaTimeTicks + leftOverTime);
  380.                     alternativeTicks += deltaTimeTicksLong;
  381.                     leftOverTime = deltaTimeTicks - deltaTimeTicksLong;
  382.                     now_t = alternativeTicks;
  383.                 }
  384.                 if (thingsDone == 0 || now_t < deadline) {
  385.                     thingsDone += DoWhatIsNeededNow(queue, now_t, deadline);
  386.                 }
  387.             }
  388. #if !UNITY_5_3_OR_NEWER
  389.             __lastUpdate = NowRealTicks;
  390. #endif
  391.         }
  392. #if !UNITY_5_3_OR_NEWER
  393.         private long __lastUpdate = 0;
  394. #endif
  395.  
  396.         int DoWhatIsNeededNow(List<ScheduledTask> a_queue, long now_t, long deadline) {
  397.             bool tryToDoMore;
  398.             int thingsDone = 0;
  399.             do {
  400.                 tryToDoMore = false;
  401.                 if (a_queue.Count > 0 && a_queue[0].when <= now_t) {
  402.                     if (_currentlyDoing.Count == 0) {
  403.                         // the things to do in the queue might add to the queue, so to prevent infinite looping...
  404.                         // separate out the elements to do right now
  405.                         for (int i = 0; i < a_queue.Count; ++i) {
  406.                             if (a_queue[i].when > now_t) { break; }
  407.                             _currentlyDoing.Add(a_queue[i]);
  408.                         }
  409.                         // if there's nothing to do, get out of this potential loop
  410.                         if (_currentlyDoing.Count == 0) { break; }
  411.                         a_queue.RemoveRange(0, _currentlyDoing.Count);
  412.                         tryToDoMore = false;
  413.                     }
  414.                     // do what is scheduled to do right now
  415.                     while (currentlyDoneThingIndex < _currentlyDoing.Count) {
  416.                         ScheduledTask todo = _currentlyDoing[currentlyDoneThingIndex++];
  417.                         // if DoActivate adds to the queue, it won't get executed this cycle
  418.                         NonStandard.ActivateAnything.DoActivate(todo.what, this, todo.who, todo.activate);
  419.                         ++thingsDone;
  420.                         // if it took too long to do that thing, stop and hold the rest of the things to do till later.
  421.                         if (maxTicksPerUpdate >= 0 && NowRealTicks > deadline) {
  422.                             break;
  423.                         }
  424.                     }
  425.                     if (currentlyDoneThingIndex >= _currentlyDoing.Count) {
  426.                         _currentlyDoing.Clear();
  427.                         currentlyDoneThingIndex = 0;
  428.                         tryToDoMore = NowRealTicks < deadline && a_queue.Count > 0;
  429.                     }
  430.                 }
  431.             } while (tryToDoMore);
  432.             return thingsDone;
  433.         }
  434.     }
  435.  
  436.     /// This class serves as a store for static Utility functions related to 'activating things'.
  437.     public static class ActivateAnything {
  438.         /// <summary>'Activates' something, in a ways that is possibly specific to circumstances.</summary>
  439.         /// <param name="whatToActivate">what needs to be activated. In "The straw that broke the camel's back", this is the "camel's back", which we can assume is breaking (or unbreaking?) during this function.</param>
  440.         /// <param name="causedActivate">what triggered the activation. In "The straw that broke the camel's back", this is "the straw". Depending on the straw, breaking may happen differently.</param>
  441.         /// <param name="doingActivate">what is doing the activation. In "The straw that broke the camel's back", this is "the camel".</param>
  442.         /// <param name="activate">whether to activate or deactivate. In "The straw that broke the camel's back", this is whether to break or unbreak the-camel's-back.</param>
  443.         /// <param name="delayInSeconds">Delay in seconds.</param>
  444.         public static void DoActivate(
  445.             object whatToActivate, object causedActivate, object doingActivate, bool activate,
  446.             float delayInSeconds
  447.         ) {
  448.             if (delayInSeconds <= 0) {
  449.                 DoActivate(whatToActivate, causedActivate, doingActivate, activate);
  450.             } else {
  451.                 Chrono.setTimeout(() => {
  452.                     DoActivate(whatToActivate, causedActivate, doingActivate, activate);
  453.                 }, (long)(delayInSeconds * 1000));
  454.             }
  455.         }
  456.  
  457.         /// <summary>'Activates' something, in a ways that is possibly specific to circumstances.</summary>
  458.         /// <param name="whatToActivate">what needs to be activated. In "The straw that broke the camel's back", this is the "camel's back", which we can assume is breaking (or unbreaking?) during this function.</param>
  459.         /// <param name="causedActivate">what triggered the activation. In "The straw that broke the camel's back", this is "the (1?) straw". Depending on the straw, or straw count, breaking may happen differently.</param>
  460.         /// <param name="doingActivate">what is doing the activation. In "The straw that broke the camel's back", this is "the camel", or possibly, "the straw".</param>
  461.         /// <param name="activate">whether to activate or deactivate. In "The straw that broke the camel's back", this is whether to break or unbreak the-camel's-back.</param>
  462.         public static void DoActivate(
  463.             object whatToActivate, object causedActivate, object doingActivate, bool activate
  464.         ) {
  465.             if (whatToActivate == null) { Chrono.Log("Don't know how to activate null"); return; }
  466.             if (whatToActivate is IReference) {
  467.                 whatToActivate = ((IReference)whatToActivate).Dereference();
  468.             }
  469.             System.Type type = whatToActivate.GetType();
  470.             if (typeof(System.Action).IsAssignableFrom(type)) {
  471.                 System.Action a = whatToActivate as System.Action;
  472.                 a.Invoke();
  473. #if UNITY_5_3_OR_NEWER
  474.             } else if (typeof(UnityEngine.Events.UnityEvent).IsAssignableFrom(type)) {
  475.                 UnityEngine.Events.UnityEvent a = whatToActivate as UnityEngine.Events.UnityEvent;
  476.                 a.Invoke();
  477.             } else if (type == typeof(Transform)) {
  478.                 Transform targetLocation = ConvertToTransform(whatToActivate);
  479.                 Transform toMove = ConvertToTransform(causedActivate);
  480.                 if (toMove != null) {
  481.                     toMove.position = targetLocation.position;
  482.                 }
  483.             } else if (type == typeof(AudioClip) || type == typeof(AudioSource)) {
  484.                 AudioSource asource = null;
  485.                 if (type == typeof(AudioSource)) {
  486.                     asource = whatToActivate as AudioSource;
  487.                 }
  488.                 if (asource == null) {
  489.                     GameObject go = ConvertToGameObject(doingActivate);
  490.                     if (go != null) {
  491.                         asource = go.AddComponent<AudioSource>();
  492.                     } else {
  493.                         throw new System.Exception("can't create audio without a game object to put it on.");
  494.                     }
  495.                 }
  496.                 if (type == typeof(AudioClip)) {
  497.                     asource.clip = whatToActivate as AudioClip;
  498.                 }
  499.                 if (activate) {
  500.                     asource.Play();
  501.                 } else {
  502.                     asource.Stop();
  503.                 }
  504.             } else if (type == typeof(ParticleSystem)) {
  505.                 ParticleSystem ps = whatToActivate as ParticleSystem;
  506.                 if (activate) {
  507.                     Transform t = ps.transform;
  508.                     GameObject go = ConvertToGameObject(doingActivate);
  509.                     t.position = go.transform.position;
  510.                     t.rotation = go.transform.rotation;
  511.                     ParticleSystem.ShapeModule sm = ps.shape;
  512.                     if (sm.shapeType == ParticleSystemShapeType.Mesh) {
  513.                         sm.mesh = go.GetComponent<MeshFilter>().mesh;
  514.                         sm.scale = go.transform.lossyScale;
  515.                     } else if (sm.shapeType == ParticleSystemShapeType.MeshRenderer) {
  516.                         sm.meshRenderer = go.GetComponent<MeshRenderer>();
  517.                     }
  518.                     ps.Play();
  519.                 } else {
  520.                     ps.Stop();
  521.                 }
  522.             } else if (type == typeof(GameObject)) {
  523.                 GameObject go = (whatToActivate as GameObject);
  524.                 if (go == null) {
  525.                     Debug.LogWarning("GameObject destroyed?");
  526.                 } else {
  527.                     go.SetActive(activate);
  528.                 }
  529.             } else if (type == typeof(UnityEngine.Color)) {
  530.                 GameObject go = ConvertToGameObject(doingActivate);
  531.                 RememberedOriginalColor r = go.GetComponent<RememberedOriginalColor>();
  532.                 if (activate) {
  533.                     Color c = (Color)whatToActivate;
  534.                     if (r == null) {
  535.                         r = go.AddComponent<RememberedOriginalColor>();
  536.                         r.oldColor = go.GetComponent<Renderer>().material.color;
  537.                     }
  538.                     go.GetComponent<Renderer>().material.color = c;
  539.                 } else {
  540.                     if (r != null) {
  541.                         go.GetComponent<Renderer>().material.color = r.oldColor;
  542.                         UnityEngine.Object.Destroy(r);
  543.                     }
  544.                 }
  545.             } else if (type == typeof(UnityEngine.Material)) {
  546.                 GameObject go = ConvertToGameObject(doingActivate);
  547.                 RememberedOriginalMaterial r = go.GetComponent<RememberedOriginalMaterial>();
  548.                 if (activate) {
  549.                     Material m = whatToActivate as Material;
  550.                     if (r == null) {
  551.                         r = go.AddComponent<RememberedOriginalMaterial>();
  552.                         r.oldMaterial = go.GetComponent<Renderer>().material;
  553.                     }
  554.                     go.GetComponent<Renderer>().material = m;
  555.                 } else {
  556.                     if (r != null) {
  557.                         go.GetComponent<Renderer>().material = r.oldMaterial;
  558.                         UnityEngine.Object.Destroy(r);
  559.                     }
  560.                 }
  561.             } else if (typeof(IEnumerable).IsAssignableFrom(type)) {
  562.                 IEnumerable ienum = whatToActivate as IEnumerable;
  563.                 IEnumerator iter = ienum.GetEnumerator();
  564.                 while (iter.MoveNext()) {
  565.                     DoActivate(iter.Current, causedActivate, doingActivate, activate);
  566.                 }
  567.             } else if (type == typeof(Animation)) {
  568.                 if (activate) {
  569.                     (whatToActivate as Animation).Play();
  570.                 } else {
  571.                     (whatToActivate as Animation).Stop();
  572.                 }
  573. #endif
  574.             } else {
  575.                 System.Reflection.MethodInfo[] m = type.GetMethods();
  576.                 bool invoked = false;
  577.                 for (int i = 0; i < m.Length; ++i) {
  578.                     System.Reflection.MethodInfo method = m[i];
  579.                     if ((activate && method.Name == "DoActivateTrigger")
  580.                     || (!activate && method.Name == "DoDeactivateTrigger")) {
  581.                         switch (method.GetParameters().Length) {
  582.                         case 0: method.Invoke(whatToActivate, new object[] { }); invoked = true; break;
  583.                         case 1: method.Invoke(whatToActivate, new object[] { causedActivate }); invoked = true; break;
  584.                         case 2: method.Invoke(whatToActivate, new object[] { causedActivate, doingActivate }); invoked = true; break;
  585.                         }
  586.                         break;
  587.                     }
  588.                 }
  589.                 if (!invoked) {
  590.                     Chrono.Log("Don't know how to " + ((activate) ? "DoActivateTrigger" : "DoDeactivateTrigger") + " a \'" + type + "\' (" + whatToActivate + ") with \'" + doingActivate + "\', triggered by \'" + causedActivate + "\'", true);
  591.                 }
  592.             }
  593.         }
  594. #if UNITY_5_3_OR_NEWER
  595.         public class RememberedOriginalMaterial : MonoBehaviour { public Material oldMaterial; }
  596.         public class RememberedOriginalColor : MonoBehaviour { public Color oldColor; }
  597.  
  598.         public static GameObject ConvertToGameObject(object obj) {
  599.             if (obj is GameObject) { return obj as GameObject; }
  600.             if (obj is Component) { return (obj as Component).gameObject; }
  601.             if (obj is Collision) { return (obj as Collision).gameObject; }
  602.             if (obj is Collision2D) { return (obj as Collision2D).gameObject; }
  603.             return null;
  604.         }
  605.  
  606.         public static Transform ConvertToTransform(object obj) {
  607.             if (obj is Transform) { return obj as Transform; }
  608.             GameObject go = ConvertToGameObject(obj);
  609.             if (go != null) { return go.transform; }
  610.             return null;
  611.         }
  612. #endif
  613.     }
  614.  
  615.     public interface IReference { object Dereference(); }
  616.  
  617. #if UNITY_5_3_OR_NEWER
  618.     [System.Serializable]
  619.     public struct ObjectPtr : IReference {
  620.         public Object data;
  621.         public Object Data { get { return data; } set { data = value; } }
  622.         public object Dereference() {
  623.             return data;
  624.         }
  625.         public TYPE GetAs<TYPE>() where TYPE : class { return data as TYPE; }
  626.     }
  627. #endif
  628.  
  629. #if UNITY_EDITOR
  630.     // used to get lists of classes for the ObjectPtr property
  631.     public static class Reflection {
  632.         public static System.Type[] GetTypesInNamespace(string nameSpace, bool includeComponentTypes = false, System.Reflection.Assembly assembly = null) {
  633.             if (assembly == null) {
  634.                 assembly = System.Reflection.Assembly.GetExecutingAssembly();
  635.             }
  636.             System.Type[] types = assembly.GetTypes().Where(t =>
  637.                 System.String.Equals(t.Namespace, nameSpace, System.StringComparison.Ordinal)
  638.                 && (includeComponentTypes || !t.ToString().Contains('+'))).ToArray();
  639.             return types;
  640.         }
  641.         public static string CleanFront(string str, string trimMe) {
  642.             if (str.StartsWith(trimMe)) { return str.Substring(trimMe.Length); }
  643.             return str;
  644.         }
  645.         public static List<string> TypeNamesCleaned(System.Type[] validTypes, string namespaceToClean) {
  646.             List<string> list = new List<string>();
  647.             for (int i = 0; i < validTypes.Length; ++i) {
  648.                 string typename = validTypes[i].ToString();
  649.                 typename = CleanFront(typename, namespaceToClean + ".");
  650.                 list.Add(typename);
  651.             }
  652.             return list;
  653.         }
  654.  
  655.         public static T EditorGUI_EnumPopup<T>(Rect _position, T value) {
  656.             System.Type t = typeof(T);
  657.             if (t.IsEnum) {
  658.                 string[] names = System.Enum.GetNames(t);
  659.                 string thisone = value.ToString();
  660.                 int index = System.Array.IndexOf(names, thisone);
  661.                 index = EditorGUI.Popup(_position, index, names);
  662.                 value = (T)System.Enum.Parse(t, names[index]);
  663.             }
  664.             return value;
  665.         }
  666.     }
  667. #endif
  668. }
  669.  
  670. #if UNITY_EDITOR
  671. // used to create scriptable objects with the ObjectPtr property
  672. public static class ScriptableObjectUtility {
  673.     /// This makes it easy to create, name and place unique new ScriptableObject asset files.
  674.     public static T CreateAsset<T>() where T : ScriptableObject { return CreateAsset(typeof(T)) as T; }
  675.     public static ScriptableObject CreateAsset(System.Type t, string filename = "", string path = "") {
  676.         ScriptableObject asset = ScriptableObject.CreateInstance(t);
  677.         string whereItWasSaved = SaveScriptableObjectAsAsset(asset, filename, path);
  678.         asset = Resources.Load(whereItWasSaved, t) as ScriptableObject;
  679.         return asset;
  680.     }
  681.  
  682.     public static string SaveScriptableObjectAsAsset(ScriptableObject asset, string filename = "", string path = "") {
  683.         System.Type t = asset.GetType();
  684.         if (path == "") {
  685.             path = AssetDatabase.GetAssetPath(Selection.activeObject);
  686.             if (path == "") {
  687.                 path = UnityEngine.SceneManagement.SceneManager.GetActiveScene().path;//"Assets";
  688.                 Debug.Log(path);
  689.                 int idx = path.LastIndexOf("/");
  690.                 if (idx < 0) {
  691.                     path = "Assets";
  692.                 } else {
  693.                     path = path.Substring(0, idx);
  694.                     if (filename == "") {
  695.                         string typename = t.ToString();
  696.                         int idx2 = typename.LastIndexOf(".");
  697.                         if (idx > 0) { typename = typename.Substring(idx2); }
  698.                         filename = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name + typename + ".asset";
  699.                     }
  700.                     Debug.Log(path + " //// " + filename);
  701.                 }
  702.                 //              Debug.Log(UnityEngine.SceneManagement.SceneManager.GetActiveScene().path);
  703.             } else if (System.IO.Path.GetExtension(path) != "") {
  704.                 path = path.Replace(System.IO.Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
  705.             }
  706.         }
  707.         if (filename.Length == 0) { filename = "New " + t.ToString() + ".asset"; }
  708.         string fullpath = path + "/" + filename;
  709.         string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(fullpath);
  710.         AssetDatabase.CreateAsset(asset, assetPathAndName);
  711.         AssetDatabase.SaveAssets();
  712.         AssetDatabase.Refresh();
  713.         EditorUtility.FocusProjectWindow();
  714.         Selection.activeObject = asset;
  715.         Debug.Log("saved " + fullpath);
  716.         return fullpath;
  717.     }
  718. }
  719.  
  720. // enables the ObjectPtr property, not fully utilized by Timer, but certainly utilized by NonStandardAssets
  721. [CustomPropertyDrawer(typeof(NonStandard.ObjectPtr))]
  722. public class PropertyDrawer_ObjectPtr : PropertyDrawer {
  723.     delegate Object SelectNextObjectFunction();
  724.     public static bool showLabel = true;
  725.     public int choice = 0;
  726.     string[] choices_name = { };
  727.     SelectNextObjectFunction[] choices_selectFunc = { };
  728.     Object choicesAreFor;
  729.     System.Type[] possibleResponses;
  730.     string[] cached_typeCreationList_names;
  731.     SelectNextObjectFunction[] cached_TypeCreationList_function;
  732.  
  733.     public static string setToNull = "set to null", delete = "delete";
  734.     public static float defaultOptionWidth = 16, defaultLabelWidth = 48, unitHeight = 16;
  735.     /// <summary>The namespaces to get default selectable classes from</summary>
  736.     protected virtual string[] GetNamespacesForNewComponentOptions() { return null; }
  737.  
  738.     public override float GetPropertyHeight(SerializedProperty _property, GUIContent label) {
  739.         return StandardCalcPropertyHeight();
  740.     }
  741.  
  742.     public static float StandardCalcPropertyHeight() {
  743.         // SerializedProperty asset = _property.FindPropertyRelative("data");
  744.         return unitHeight;//base.GetPropertyHeight (asset, label);
  745.     }
  746.  
  747.     /// <summary>
  748.     /// When the ObjectPtr points to nothing, this method generates the objects that can be created by default
  749.     /// </summary>
  750.     /// <param name="self">Self.</param>
  751.     /// <param name="names">Names.</param>
  752.     /// <param name="functions">Functions.</param>
  753.     private void GenerateTypeCreationList(Component self, out string[] names, out SelectNextObjectFunction[] functions) {
  754.         List<string> list = new List<string>();
  755.         List<SelectNextObjectFunction> list_of_data = new List<SelectNextObjectFunction>();
  756.         string[] theList = GetNamespacesForNewComponentOptions();
  757.         if (theList != null) {
  758.             for (int i = 0; i < theList.Length; ++i) {
  759.                 string namespaceName = theList[i];
  760.                 possibleResponses = NonStandard.Reflection.GetTypesInNamespace(namespaceName);
  761.                 list.AddRange(NonStandard.Reflection.TypeNamesCleaned(possibleResponses, namespaceName));
  762.                 for (int t = 0; t < possibleResponses.Length; t++) {
  763.                     System.Type nextT = possibleResponses[t];
  764.                     list_of_data.Add(() => {
  765.                         return CreateSelectedClass(nextT, self);
  766.                     });
  767.                 }
  768.             }
  769.         }
  770.         list.Insert(0, (theList != null) ? "<-- select Object or create..." : "<-- select Object");
  771.         list_of_data.Insert(0, null);
  772.         names = list.ToArray();
  773.         functions = list_of_data.ToArray();
  774.     }
  775.  
  776.     private void CleanTypename(ref string typename) {
  777.         int lastDot = typename.LastIndexOf('.');
  778.         if (lastDot >= 0) { typename = typename.Substring(lastDot + 1); }
  779.     }
  780.  
  781.     private void GenerateChoicesForSelectedObject(Component self, out string[] names, out SelectNextObjectFunction[] functions) {
  782.         List<string> components = new List<string>();
  783.         List<SelectNextObjectFunction> nextSelectionFunc = new List<SelectNextObjectFunction>();
  784.         string typename = choicesAreFor.GetType().ToString();
  785.         CleanTypename(ref typename);
  786.         components.Add(typename);
  787.         nextSelectionFunc.Add(null);
  788.         GameObject go = choicesAreFor as GameObject;
  789.         bool addSetToNull = true;
  790.         Object addDelete = null;
  791.         if (go != null) {
  792.             Component[] c = go.GetComponents<Component>();
  793.             for (int i = 0; i < c.Length; i++) {
  794.                 Component comp = c[i];
  795.                 if (comp != self) {
  796.                     typename = comp.GetType().ToString();
  797.                     CleanTypename(ref typename);
  798.                     components.Add(typename);
  799.                     nextSelectionFunc.Add(() => { return comp; });
  800.                 }
  801.             }
  802.             addSetToNull = true;
  803.         } else if (choicesAreFor is Component) {
  804.             components.Add(".gameObject");
  805.             GameObject gob = (choicesAreFor as Component).gameObject;
  806.             nextSelectionFunc.Add(() => { return gob; });
  807.             addSetToNull = true;
  808.             addDelete = choicesAreFor;
  809.         }
  810.         if (addSetToNull) {
  811.             components.Add(setToNull);
  812.             nextSelectionFunc.Add(() => {
  813.                 choice = 0; return null;
  814.             });
  815.         }
  816.         if (addDelete != null) {
  817.             components.Add(delete);
  818.             nextSelectionFunc.Add(() => {
  819.                 //Object.DestroyImmediate(addDelete);
  820.                 itemsToCleanup.Add(addDelete);
  821.                 choice = 0; return null;
  822.             });
  823.         }
  824.         names = components.ToArray();
  825.         functions = nextSelectionFunc.ToArray();
  826.     }
  827.  
  828.     [ExecuteInEditMode]
  829.     private class IndirectCleaner : MonoBehaviour {
  830.         public List<Object> itemsToCleanup;
  831.         private void Update() {
  832.             for (int i = itemsToCleanup.Count - 1; i >= 0; --i) {
  833.                 Object o = itemsToCleanup[i];
  834.                 if (o != null) { Object.DestroyImmediate(o); }
  835.             }
  836.             itemsToCleanup.Clear();
  837.             DestroyImmediate(this); // cleaning lady disposes of herself too
  838.         }
  839.     }
  840.     private List<Object> itemsToCleanup = new List<Object>();
  841.     private void RequestCleanup(Component self) {
  842.         // if any items need to be deleted, don't do it now! the UI is in the middle of being drawn!
  843.         // create a separate process that will do it for you
  844.         IndirectCleaner cleaner = self.gameObject.AddComponent<IndirectCleaner>();
  845.         cleaner.itemsToCleanup = this.itemsToCleanup;
  846.     }
  847.  
  848.     /// <summary>called right after an object is assigned</summary>
  849.     public virtual Object FilterImmidiate(Object obj, Component self) {
  850.         return obj;
  851.     }
  852.  
  853.     /// <summary>called right after a new component is created to be assigned</summary>
  854.     protected virtual Object FilterNewComponent(System.Type nextT, Component self, Component newlyCreatedComponent) {
  855.         return newlyCreatedComponent;
  856.     }
  857.  
  858.     /// <summary>called just before UI is finished. This is the last chance to adjust the new setting.</summary>
  859.     public virtual Object FilterFinal(Object newObjToReference, Object prevObj, Component self) {
  860.         return newObjToReference;
  861.     }
  862.  
  863.     private Object CreateSelectedClass(System.Type nextT, Component self) {
  864.         Object obj = null;
  865.         if (self != null && self.gameObject != null) {
  866.             GameObject go = self.gameObject;
  867.             if (nextT.IsSubclassOf(typeof(ScriptableObject))) {
  868.                 obj = ScriptableObjectUtility.CreateAsset(nextT);
  869.             } else {
  870.                 Component newComponent = go.AddComponent(nextT);
  871.                 obj = FilterNewComponent(nextT, self, newComponent);
  872.             }
  873.         }
  874.         return obj;
  875.     }
  876.  
  877.     public static SerializedProperty ObjectPtrAsset(SerializedProperty _property) {
  878.         SerializedProperty asset = _property.FindPropertyRelative("data");
  879.         //asset = asset.FindPropertyRelative("data");
  880.         return asset;
  881.     }
  882.  
  883.     public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label) {
  884.         EditorGUI.BeginProperty(_position, GUIContent.none, _property);
  885.         SerializedProperty asset = ObjectPtrAsset(_property);
  886.         int oldIndent = EditorGUI.indentLevel;
  887.         EditorGUI.indentLevel = 0;
  888.         if (PropertyDrawer_ObjectPtr.showLabel) {
  889.             _position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);
  890.         }
  891.         Component self = _property.serializedObject.targetObject as Component;
  892.         if (asset != null) {
  893.             Object prevObj = asset.objectReferenceValue;
  894.             asset.objectReferenceValue = EditorGUIObjectReference(_position, asset.objectReferenceValue, self);
  895.             asset.objectReferenceValue = FilterFinal(asset.objectReferenceValue, prevObj, self);
  896.             //Contingentable cself = self as Contingentable;
  897.             //if(prevObj != asset.objectReferenceValue && cself != null && cself.ContingencyRecursionCheck() != null) {
  898.             //  Debug.LogWarning("Disallowing recursion of " + asset.objectReferenceValue);
  899.             //  asset.objectReferenceValue = prevObj;
  900.             //}
  901.         }
  902.         EditorGUI.indentLevel = oldIndent;
  903.         EditorGUI.EndProperty();
  904.         if (itemsToCleanup.Count != 0) { RequestCleanup(self); }
  905.     }
  906.  
  907.     // TODO rename this to DoGUI
  908.     public virtual Object EditorGUIObjectReference(Rect _position, Object obj, Component self) {
  909.         int oldIndent = EditorGUI.indentLevel;
  910.         EditorGUI.indentLevel = 0;
  911.         obj = StandardEditorGUIObjectReference(_position, obj, self);
  912.         EditorGUI.indentLevel = oldIndent;
  913.         return obj;
  914.     }
  915.  
  916.     public Object ShowObjectPtrChoicesPopup(Rect _position, Object obj, Component self, bool recalculateChoices) {
  917.         // if the object needs to have it's alternate forms calculated
  918.         if (recalculateChoices || choicesAreFor != obj || choices_name.Length == 0) {
  919.             choicesAreFor = obj;
  920.             // if these choices are for an actual object
  921.             if (choicesAreFor != null) {
  922.                 GenerateChoicesForSelectedObject(self, out choices_name, out choices_selectFunc);
  923.                 choice = 0;
  924.             } else {
  925.                 if (cached_typeCreationList_names == null) {
  926.                     GenerateTypeCreationList(self,
  927.                         out cached_typeCreationList_names, out cached_TypeCreationList_function);
  928.                 }
  929.                 choices_name = cached_typeCreationList_names;
  930.                 choices_selectFunc = cached_TypeCreationList_function;
  931.             }
  932.         }
  933.         // give the alternate options for the object
  934.         int lastChoice = choice;
  935.         _position.x += _position.width;
  936.         _position.width = defaultOptionWidth;
  937.         choice = EditorGUI.Popup(_position, choice, choices_name);
  938.         if (lastChoice != choice) {
  939.             if (choices_selectFunc[choice] != null) {
  940.                 obj = choices_selectFunc[choice]();
  941.             }
  942.         }
  943.         return obj;
  944.     }
  945.  
  946.     public Object StandardEditorGUIObjectReference(Rect _position, Object obj, Component self) {
  947.         float originalWidth = _position.width;
  948.         _position.width = originalWidth - defaultOptionWidth;
  949.         Object prevSelection = obj;
  950.         obj = EditorGUI.ObjectField(_position, obj, typeof(Object), true);
  951.         obj = FilterImmidiate(obj, self);
  952.         obj = ShowObjectPtrChoicesPopup(_position, obj, self, obj != prevSelection);
  953.         return obj;
  954.     }
  955.  
  956.     public Object DoGUIEnumLabeledString<T>(Rect _position, Object obj, Component self,
  957.         ref T enumValue, ref string textValue) {
  958.         int oldindent = EditorGUI.indentLevel;
  959.         EditorGUI.indentLevel = 0;
  960.         Rect r = _position;
  961.         float w = defaultOptionWidth, wl = defaultLabelWidth;
  962.         r.width = wl;
  963.         enumValue = NonStandard.Reflection.EditorGUI_EnumPopup<T>(r, enumValue);
  964.         r.x += r.width;
  965.         r.width = _position.width - w - wl;
  966.         textValue = EditorGUI.TextField(r, textValue);
  967.         obj = ShowObjectPtrChoicesPopup(r, obj, self, true);
  968.         r.x += r.width;
  969.         r.width = w;
  970.         EditorGUI.indentLevel = oldindent;
  971.         return obj;
  972.     }
  973.     public Object DoGUIEnumLabeledObject<T>(Rect _position, Object obj, Component self,
  974.         ref T enumValue, ref Object objectValue) {
  975.         int oldindent = EditorGUI.indentLevel;
  976.         EditorGUI.indentLevel = 0;
  977.         Rect r = _position;
  978.         float w = defaultOptionWidth, wl = defaultLabelWidth;
  979.         r.width = wl;
  980.         enumValue = NonStandard.Reflection.EditorGUI_EnumPopup<T>(r, enumValue);
  981.         r.x += r.width;
  982.         r.width = _position.width - w - wl;
  983.         objectValue = EditorGUI.ObjectField(r, objectValue, typeof(Object), true);
  984.         obj = FilterImmidiate(obj, self);
  985.         obj = ShowObjectPtrChoicesPopup(r, obj, self, true);
  986.         r.x += r.width;
  987.         r.width = w;
  988.         EditorGUI.indentLevel = oldindent;
  989.         return obj;
  990.     }
  991. }
  992. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement