暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

Timer.cs 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. data.update = null;
  142. data.isListening = false;
  143. }
  144. public bool IsListening(GraphPointer pointer)
  145. {
  146. return pointer.GetElementData<Data>(this).isListening;
  147. }
  148. private void TriggerUpdate(GraphReference reference)
  149. {
  150. using (var flow = Flow.New(reference))
  151. {
  152. Update(flow);
  153. }
  154. }
  155. private ControlOutput Start(Flow flow)
  156. {
  157. var data = flow.stack.GetElementData<Data>(this);
  158. data.elapsed = 0;
  159. data.duration = flow.GetValue<float>(duration);
  160. data.active = true;
  161. data.paused = false;
  162. data.unscaled = flow.GetValue<bool>(unscaledTime);
  163. AssignMetrics(flow, data);
  164. return started;
  165. }
  166. private ControlOutput Pause(Flow flow)
  167. {
  168. var data = flow.stack.GetElementData<Data>(this);
  169. data.paused = true;
  170. return null;
  171. }
  172. private ControlOutput Resume(Flow flow)
  173. {
  174. var data = flow.stack.GetElementData<Data>(this);
  175. data.paused = false;
  176. return null;
  177. }
  178. private ControlOutput Toggle(Flow flow)
  179. {
  180. var data = flow.stack.GetElementData<Data>(this);
  181. if (!data.active)
  182. {
  183. return Start(flow);
  184. }
  185. else
  186. {
  187. data.paused = !data.paused;
  188. return null;
  189. }
  190. }
  191. private void AssignMetrics(Flow flow, Data data)
  192. {
  193. flow.SetValue(elapsedSeconds, data.elapsed);
  194. flow.SetValue(elapsedRatio, Mathf.Clamp01(data.elapsed / data.duration));
  195. flow.SetValue(remainingSeconds, Mathf.Max(0, data.duration - data.elapsed));
  196. flow.SetValue(remainingRatio, Mathf.Clamp01((data.duration - data.elapsed) / data.duration));
  197. }
  198. public void Update(Flow flow)
  199. {
  200. var data = flow.stack.GetElementData<Data>(this);
  201. if (!data.active || data.paused)
  202. {
  203. return;
  204. }
  205. data.elapsed += data.unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
  206. data.elapsed = Mathf.Min(data.elapsed, data.duration);
  207. AssignMetrics(flow, data);
  208. var stack = flow.PreserveStack();
  209. flow.Invoke(tick);
  210. if (data.elapsed >= data.duration)
  211. {
  212. data.active = false;
  213. flow.RestoreStack(stack);
  214. flow.Invoke(completed);
  215. }
  216. flow.DisposePreservedStack(stack);
  217. }
  218. }
  219. }