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.

Cooldown.cs 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using System;
  2. using UnityEngine;
  3. namespace Unity.VisualScripting
  4. {
  5. /// <summary>
  6. /// Runs a cooldown timer to throttle flow and outputs remaining measurements.
  7. /// </summary>
  8. [UnitCategory("Time")]
  9. [TypeIcon(typeof(Timer))]
  10. [UnitOrder(8)]
  11. public sealed class Cooldown : Unit, IGraphElementWithData, IGraphEventListener
  12. {
  13. public sealed class Data : IGraphElementData
  14. {
  15. public float remaining;
  16. public float duration;
  17. public bool unscaled;
  18. public bool isReady => remaining <= 0;
  19. public Delegate update;
  20. public bool isListening;
  21. }
  22. /// <summary>
  23. /// The moment at which to try using the cooldown.
  24. /// </summary>
  25. [DoNotSerialize]
  26. [PortLabelHidden]
  27. public ControlInput enter { get; private set; }
  28. /// <summary>
  29. /// Trigger to force reset the cooldown.
  30. /// </summary>
  31. [DoNotSerialize]
  32. public ControlInput reset { get; private set; }
  33. /// <summary>
  34. /// The total duration of the cooldown.
  35. /// </summary>
  36. [DoNotSerialize]
  37. public ValueInput duration { get; private set; }
  38. /// <summary>
  39. /// Whether to ignore the time scale.
  40. /// </summary>
  41. [DoNotSerialize]
  42. [PortLabel("Unscaled")]
  43. public ValueInput unscaledTime { get; private set; }
  44. /// <summary>
  45. /// Called upon entry when the cooldown is ready.
  46. /// </summary>
  47. [DoNotSerialize]
  48. [PortLabel("Ready")]
  49. public ControlOutput exitReady { get; private set; }
  50. /// <summary>
  51. /// Called upon entry when the cooldown is not yet ready.
  52. /// </summary>
  53. [DoNotSerialize]
  54. [PortLabel("Not Ready")]
  55. public ControlOutput exitNotReady { get; private set; }
  56. /// <summary>
  57. /// Called each frame while the cooldown timer is active.
  58. /// </summary>
  59. [DoNotSerialize]
  60. public ControlOutput tick { get; private set; }
  61. /// <summary>
  62. /// Called when the cooldown timer reaches zero.
  63. /// </summary>
  64. [DoNotSerialize]
  65. [PortLabel("Completed")]
  66. public ControlOutput becameReady { get; private set; }
  67. /// <summary>
  68. /// The number of seconds remaining until the cooldown is ready.
  69. /// </summary>
  70. [DoNotSerialize]
  71. [PortLabel("Remaining")]
  72. public ValueOutput remainingSeconds { get; private set; }
  73. /// <summary>
  74. /// The proportion of the duration remaining until the cooldown is ready (0-1).
  75. /// </summary>
  76. [DoNotSerialize]
  77. [PortLabel("Remaining %")]
  78. public ValueOutput remainingRatio { get; private set; }
  79. protected override void Definition()
  80. {
  81. enter = ControlInput(nameof(enter), Enter);
  82. reset = ControlInput(nameof(reset), Reset);
  83. duration = ValueInput(nameof(duration), 1f);
  84. unscaledTime = ValueInput(nameof(unscaledTime), false);
  85. exitReady = ControlOutput(nameof(exitReady));
  86. exitNotReady = ControlOutput(nameof(exitNotReady));
  87. tick = ControlOutput(nameof(tick));
  88. becameReady = ControlOutput(nameof(becameReady));
  89. remainingSeconds = ValueOutput<float>(nameof(remainingSeconds));
  90. remainingRatio = ValueOutput<float>(nameof(remainingRatio));
  91. Requirement(duration, enter);
  92. Requirement(unscaledTime, enter);
  93. Succession(enter, exitReady);
  94. Succession(enter, exitNotReady);
  95. Succession(enter, tick);
  96. Succession(enter, becameReady);
  97. Assignment(enter, remainingSeconds);
  98. Assignment(enter, remainingRatio);
  99. }
  100. public IGraphElementData CreateData()
  101. {
  102. return new Data();
  103. }
  104. public void StartListening(GraphStack stack)
  105. {
  106. var data = stack.GetElementData<Data>(this);
  107. if (data.isListening)
  108. {
  109. return;
  110. }
  111. var reference = stack.ToReference();
  112. var hook = new EventHook(EventHooks.Update, stack.machine);
  113. Action<EmptyEventArgs> update = args => TriggerUpdate(reference);
  114. EventBus.Register(hook, update);
  115. data.update = update;
  116. data.isListening = true;
  117. }
  118. public void StopListening(GraphStack stack)
  119. {
  120. var data = stack.GetElementData<Data>(this);
  121. if (!data.isListening)
  122. {
  123. return;
  124. }
  125. var hook = new EventHook(EventHooks.Update, stack.machine);
  126. EventBus.Unregister(hook, data.update);
  127. data.update = null;
  128. data.isListening = false;
  129. }
  130. public bool IsListening(GraphPointer pointer)
  131. {
  132. return pointer.GetElementData<Data>(this).isListening;
  133. }
  134. private void TriggerUpdate(GraphReference reference)
  135. {
  136. using (var flow = Flow.New(reference))
  137. {
  138. Update(flow);
  139. }
  140. }
  141. private ControlOutput Enter(Flow flow)
  142. {
  143. var data = flow.stack.GetElementData<Data>(this);
  144. if (data.isReady)
  145. {
  146. return Reset(flow);
  147. }
  148. else
  149. {
  150. return exitNotReady;
  151. }
  152. }
  153. private ControlOutput Reset(Flow flow)
  154. {
  155. var data = flow.stack.GetElementData<Data>(this);
  156. data.duration = flow.GetValue<float>(duration);
  157. data.remaining = data.duration;
  158. data.unscaled = flow.GetValue<bool>(unscaledTime);
  159. return exitReady;
  160. }
  161. private void AssignMetrics(Flow flow, Data data)
  162. {
  163. flow.SetValue(remainingSeconds, data.remaining);
  164. flow.SetValue(remainingRatio, Mathf.Clamp01(data.remaining / data.duration));
  165. }
  166. public void Update(Flow flow)
  167. {
  168. var data = flow.stack.GetElementData<Data>(this);
  169. if (data.isReady)
  170. {
  171. return;
  172. }
  173. data.remaining -= data.unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
  174. data.remaining = Mathf.Max(0f, data.remaining);
  175. AssignMetrics(flow, data);
  176. var stack = flow.PreserveStack();
  177. flow.Invoke(tick);
  178. if (data.isReady)
  179. {
  180. flow.RestoreStack(stack);
  181. flow.Invoke(becameReady);
  182. }
  183. flow.DisposePreservedStack(stack);
  184. }
  185. }
  186. }