No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Timer.cs 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. using System;
  2. using UnityEngine;
  3. namespace Unity.VisualScripting
  4. {
  5. /// <summary>
  6. /// Runs a timer and outputs elapsed and remaining measurements.
  7. /// </summary>
  8. [UnitCategory("Time")]
  9. [UnitOrder(7)]
  10. public sealed class Timer : Unit, IGraphElementWithData, IGraphEventListener
  11. {
  12. public sealed class Data : IGraphElementData
  13. {
  14. public float elapsed;
  15. public float duration;
  16. public bool active;
  17. public bool paused;
  18. public bool unscaled;
  19. public Delegate update;
  20. public bool isListening;
  21. }
  22. /// <summary>
  23. /// The moment at which to start the timer.
  24. /// If the timer is already started, this will reset it.
  25. /// If the timer is paused, this will resume it.
  26. /// </summary>
  27. [DoNotSerialize]
  28. public ControlInput start { get; private set; }
  29. /// <summary>
  30. /// Trigger to pause the timer.
  31. /// </summary>
  32. [DoNotSerialize]
  33. public ControlInput pause { get; private set; }
  34. /// <summary>
  35. /// Trigger to resume the timer.
  36. /// </summary>
  37. [DoNotSerialize]
  38. public ControlInput resume { get; private set; }
  39. /// <summary>
  40. /// Trigger to toggle the timer.
  41. /// If it is idle, it will start.
  42. /// If it is active, it will pause.
  43. /// If it is paused, it will resume.
  44. /// </summary>
  45. [DoNotSerialize]
  46. public ControlInput toggle { get; private set; }
  47. /// <summary>
  48. /// The total duration of the timer.
  49. /// </summary>
  50. [DoNotSerialize]
  51. public ValueInput duration { get; private set; }
  52. /// <summary>
  53. /// Whether to ignore the time scale.
  54. /// </summary>
  55. [DoNotSerialize]
  56. [PortLabel("Unscaled")]
  57. public ValueInput unscaledTime { get; private set; }
  58. /// <summary>
  59. /// Called when the timer is started.co
  60. /// </summary>
  61. [DoNotSerialize]
  62. public ControlOutput started { get; private set; }
  63. /// <summary>
  64. /// Called each frame while the timer is active.
  65. /// </summary>
  66. [DoNotSerialize]
  67. public ControlOutput tick { get; private set; }
  68. /// <summary>
  69. /// Called when the timer completes.
  70. /// </summary>
  71. [DoNotSerialize]
  72. public ControlOutput completed { get; private set; }
  73. /// <summary>
  74. /// The number of seconds elapsed since the timer started.
  75. /// </summary>
  76. [DoNotSerialize]
  77. [PortLabel("Elapsed")]
  78. public ValueOutput elapsedSeconds { get; private set; }
  79. /// <summary>
  80. /// The proportion of the duration that has elapsed (0-1).
  81. /// </summary>
  82. [DoNotSerialize]
  83. [PortLabel("Elapsed %")]
  84. public ValueOutput elapsedRatio { get; private set; }
  85. /// <summary>
  86. /// The number of seconds remaining until the timer is elapsed.
  87. /// </summary>
  88. [DoNotSerialize]
  89. [PortLabel("Remaining")]
  90. public ValueOutput remainingSeconds { get; private set; }
  91. /// <summary>
  92. /// The proportion of the duration remaining until the timer is elapsed (0-1).
  93. /// </summary>
  94. [DoNotSerialize]
  95. [PortLabel("Remaining %")]
  96. public ValueOutput remainingRatio { get; private set; }
  97. protected override void Definition()
  98. {
  99. isControlRoot = true;
  100. start = ControlInput(nameof(start), Start);
  101. pause = ControlInput(nameof(pause), Pause);
  102. resume = ControlInput(nameof(resume), Resume);
  103. toggle = ControlInput(nameof(toggle), Toggle);
  104. duration = ValueInput(nameof(duration), 1f);
  105. unscaledTime = ValueInput(nameof(unscaledTime), false);
  106. started = ControlOutput(nameof(started));
  107. tick = ControlOutput(nameof(tick));
  108. completed = ControlOutput(nameof(completed));
  109. elapsedSeconds = ValueOutput<float>(nameof(elapsedSeconds));
  110. elapsedRatio = ValueOutput<float>(nameof(elapsedRatio));
  111. remainingSeconds = ValueOutput<float>(nameof(remainingSeconds));
  112. remainingRatio = ValueOutput<float>(nameof(remainingRatio));
  113. }
  114. public IGraphElementData CreateData()
  115. {
  116. return new Data();
  117. }
  118. public void StartListening(GraphStack stack)
  119. {
  120. var data = stack.GetElementData<Data>(this);
  121. if (data.isListening)
  122. {
  123. return;
  124. }
  125. var reference = stack.ToReference();
  126. var hook = new EventHook(EventHooks.Update, stack.machine);
  127. Action<EmptyEventArgs> update = args => TriggerUpdate(reference);
  128. EventBus.Register(hook, update);
  129. data.update = update;
  130. data.isListening = true;
  131. }
  132. public void StopListening(GraphStack stack)
  133. {
  134. var data = stack.GetElementData<Data>(this);
  135. if (!data.isListening)
  136. {
  137. return;
  138. }
  139. var hook = new EventHook(EventHooks.Update, stack.machine);
  140. EventBus.Unregister(hook, data.update);
  141. stack.ClearReference();
  142. data.update = null;
  143. data.isListening = false;
  144. }
  145. public bool IsListening(GraphPointer pointer)
  146. {
  147. return pointer.GetElementData<Data>(this).isListening;
  148. }
  149. private void TriggerUpdate(GraphReference reference)
  150. {
  151. using (var flow = Flow.New(reference))
  152. {
  153. Update(flow);
  154. }
  155. }
  156. private ControlOutput Start(Flow flow)
  157. {
  158. var data = flow.stack.GetElementData<Data>(this);
  159. data.elapsed = 0;
  160. data.duration = flow.GetValue<float>(duration);
  161. data.active = true;
  162. data.paused = false;
  163. data.unscaled = flow.GetValue<bool>(unscaledTime);
  164. AssignMetrics(flow, data);
  165. return started;
  166. }
  167. private ControlOutput Pause(Flow flow)
  168. {
  169. var data = flow.stack.GetElementData<Data>(this);
  170. data.paused = true;
  171. return null;
  172. }
  173. private ControlOutput Resume(Flow flow)
  174. {
  175. var data = flow.stack.GetElementData<Data>(this);
  176. data.paused = false;
  177. return null;
  178. }
  179. private ControlOutput Toggle(Flow flow)
  180. {
  181. var data = flow.stack.GetElementData<Data>(this);
  182. if (!data.active)
  183. {
  184. return Start(flow);
  185. }
  186. else
  187. {
  188. data.paused = !data.paused;
  189. return null;
  190. }
  191. }
  192. private void AssignMetrics(Flow flow, Data data)
  193. {
  194. flow.SetValue(elapsedSeconds, data.elapsed);
  195. flow.SetValue(elapsedRatio, Mathf.Clamp01(data.elapsed / data.duration));
  196. flow.SetValue(remainingSeconds, Mathf.Max(0, data.duration - data.elapsed));
  197. flow.SetValue(remainingRatio, Mathf.Clamp01((data.duration - data.elapsed) / data.duration));
  198. }
  199. public void Update(Flow flow)
  200. {
  201. var data = flow.stack.GetElementData<Data>(this);
  202. if (!data.active || data.paused)
  203. {
  204. return;
  205. }
  206. data.elapsed += data.unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
  207. data.elapsed = Mathf.Min(data.elapsed, data.duration);
  208. AssignMetrics(flow, data);
  209. var stack = flow.PreserveStack();
  210. flow.Invoke(tick);
  211. if (data.elapsed >= data.duration)
  212. {
  213. data.active = false;
  214. flow.RestoreStack(stack);
  215. flow.Invoke(completed);
  216. }
  217. flow.DisposePreservedStack(stack);
  218. }
  219. }
  220. }