Ingen beskrivning
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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. stack.ClearReference();
  128. data.update = null;
  129. data.isListening = false;
  130. }
  131. public bool IsListening(GraphPointer pointer)
  132. {
  133. return pointer.GetElementData<Data>(this).isListening;
  134. }
  135. private void TriggerUpdate(GraphReference reference)
  136. {
  137. using (var flow = Flow.New(reference))
  138. {
  139. Update(flow);
  140. }
  141. }
  142. private ControlOutput Enter(Flow flow)
  143. {
  144. var data = flow.stack.GetElementData<Data>(this);
  145. if (data.isReady)
  146. {
  147. return Reset(flow);
  148. }
  149. else
  150. {
  151. return exitNotReady;
  152. }
  153. }
  154. private ControlOutput Reset(Flow flow)
  155. {
  156. var data = flow.stack.GetElementData<Data>(this);
  157. data.duration = flow.GetValue<float>(duration);
  158. data.remaining = data.duration;
  159. data.unscaled = flow.GetValue<bool>(unscaledTime);
  160. return exitReady;
  161. }
  162. private void AssignMetrics(Flow flow, Data data)
  163. {
  164. flow.SetValue(remainingSeconds, data.remaining);
  165. flow.SetValue(remainingRatio, Mathf.Clamp01(data.remaining / data.duration));
  166. }
  167. public void Update(Flow flow)
  168. {
  169. var data = flow.stack.GetElementData<Data>(this);
  170. if (data.isReady)
  171. {
  172. return;
  173. }
  174. data.remaining -= data.unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
  175. data.remaining = Mathf.Max(0f, data.remaining);
  176. AssignMetrics(flow, data);
  177. var stack = flow.PreserveStack();
  178. flow.Invoke(tick);
  179. if (data.isReady)
  180. {
  181. flow.RestoreStack(stack);
  182. flow.Invoke(becameReady);
  183. }
  184. flow.DisposePreservedStack(stack);
  185. }
  186. }
  187. }