123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- using System;
- using UnityEngine;
-
- namespace Unity.VisualScripting
- {
- /// <summary>
- /// Runs a timer and outputs elapsed and remaining measurements.
- /// </summary>
- [UnitCategory("Time")]
- [UnitOrder(7)]
- public sealed class Timer : Unit, IGraphElementWithData, IGraphEventListener
- {
- public sealed class Data : IGraphElementData
- {
- public float elapsed;
-
- public float duration;
-
- public bool active;
-
- public bool paused;
-
- public bool unscaled;
-
- public Delegate update;
-
- public bool isListening;
- }
-
- /// <summary>
- /// The moment at which to start the timer.
- /// If the timer is already started, this will reset it.
- /// If the timer is paused, this will resume it.
- /// </summary>
- [DoNotSerialize]
- public ControlInput start { get; private set; }
-
- /// <summary>
- /// Trigger to pause the timer.
- /// </summary>
- [DoNotSerialize]
- public ControlInput pause { get; private set; }
-
- /// <summary>
- /// Trigger to resume the timer.
- /// </summary>
- [DoNotSerialize]
- public ControlInput resume { get; private set; }
-
- /// <summary>
- /// Trigger to toggle the timer.
- /// If it is idle, it will start.
- /// If it is active, it will pause.
- /// If it is paused, it will resume.
- /// </summary>
- [DoNotSerialize]
- public ControlInput toggle { get; private set; }
-
- /// <summary>
- /// The total duration of the timer.
- /// </summary>
- [DoNotSerialize]
- public ValueInput duration { get; private set; }
-
- /// <summary>
- /// Whether to ignore the time scale.
- /// </summary>
- [DoNotSerialize]
- [PortLabel("Unscaled")]
- public ValueInput unscaledTime { get; private set; }
-
- /// <summary>
- /// Called when the timer is started.co
- /// </summary>
- [DoNotSerialize]
- public ControlOutput started { get; private set; }
-
- /// <summary>
- /// Called each frame while the timer is active.
- /// </summary>
- [DoNotSerialize]
- public ControlOutput tick { get; private set; }
-
- /// <summary>
- /// Called when the timer completes.
- /// </summary>
- [DoNotSerialize]
- public ControlOutput completed { get; private set; }
-
- /// <summary>
- /// The number of seconds elapsed since the timer started.
- /// </summary>
- [DoNotSerialize]
- [PortLabel("Elapsed")]
- public ValueOutput elapsedSeconds { get; private set; }
-
- /// <summary>
- /// The proportion of the duration that has elapsed (0-1).
- /// </summary>
- [DoNotSerialize]
- [PortLabel("Elapsed %")]
- public ValueOutput elapsedRatio { get; private set; }
-
- /// <summary>
- /// The number of seconds remaining until the timer is elapsed.
- /// </summary>
- [DoNotSerialize]
- [PortLabel("Remaining")]
- public ValueOutput remainingSeconds { get; private set; }
-
- /// <summary>
- /// The proportion of the duration remaining until the timer is elapsed (0-1).
- /// </summary>
- [DoNotSerialize]
- [PortLabel("Remaining %")]
- public ValueOutput remainingRatio { get; private set; }
-
- protected override void Definition()
- {
- isControlRoot = true;
-
- start = ControlInput(nameof(start), Start);
- pause = ControlInput(nameof(pause), Pause);
- resume = ControlInput(nameof(resume), Resume);
- toggle = ControlInput(nameof(toggle), Toggle);
-
- duration = ValueInput(nameof(duration), 1f);
- unscaledTime = ValueInput(nameof(unscaledTime), false);
-
- started = ControlOutput(nameof(started));
- tick = ControlOutput(nameof(tick));
- completed = ControlOutput(nameof(completed));
-
- elapsedSeconds = ValueOutput<float>(nameof(elapsedSeconds));
- elapsedRatio = ValueOutput<float>(nameof(elapsedRatio));
-
- remainingSeconds = ValueOutput<float>(nameof(remainingSeconds));
- remainingRatio = ValueOutput<float>(nameof(remainingRatio));
- }
-
- public IGraphElementData CreateData()
- {
- return new Data();
- }
-
- public void StartListening(GraphStack stack)
- {
- var data = stack.GetElementData<Data>(this);
-
- if (data.isListening)
- {
- return;
- }
-
- var reference = stack.ToReference();
- var hook = new EventHook(EventHooks.Update, stack.machine);
- Action<EmptyEventArgs> update = args => TriggerUpdate(reference);
- EventBus.Register(hook, update);
- data.update = update;
- data.isListening = true;
- }
-
- public void StopListening(GraphStack stack)
- {
- var data = stack.GetElementData<Data>(this);
-
- if (!data.isListening)
- {
- return;
- }
-
- var hook = new EventHook(EventHooks.Update, stack.machine);
- EventBus.Unregister(hook, data.update);
- data.update = null;
- data.isListening = false;
- }
-
- public bool IsListening(GraphPointer pointer)
- {
- return pointer.GetElementData<Data>(this).isListening;
- }
-
- private void TriggerUpdate(GraphReference reference)
- {
- using (var flow = Flow.New(reference))
- {
- Update(flow);
- }
- }
-
- private ControlOutput Start(Flow flow)
- {
- var data = flow.stack.GetElementData<Data>(this);
-
- data.elapsed = 0;
- data.duration = flow.GetValue<float>(duration);
- data.active = true;
- data.paused = false;
- data.unscaled = flow.GetValue<bool>(unscaledTime);
-
- AssignMetrics(flow, data);
-
- return started;
- }
-
- private ControlOutput Pause(Flow flow)
- {
- var data = flow.stack.GetElementData<Data>(this);
-
- data.paused = true;
-
- return null;
- }
-
- private ControlOutput Resume(Flow flow)
- {
- var data = flow.stack.GetElementData<Data>(this);
-
- data.paused = false;
-
- return null;
- }
-
- private ControlOutput Toggle(Flow flow)
- {
- var data = flow.stack.GetElementData<Data>(this);
-
- if (!data.active)
- {
- return Start(flow);
- }
- else
- {
- data.paused = !data.paused;
-
- return null;
- }
- }
-
- private void AssignMetrics(Flow flow, Data data)
- {
- flow.SetValue(elapsedSeconds, data.elapsed);
- flow.SetValue(elapsedRatio, Mathf.Clamp01(data.elapsed / data.duration));
-
- flow.SetValue(remainingSeconds, Mathf.Max(0, data.duration - data.elapsed));
- flow.SetValue(remainingRatio, Mathf.Clamp01((data.duration - data.elapsed) / data.duration));
- }
-
- public void Update(Flow flow)
- {
- var data = flow.stack.GetElementData<Data>(this);
-
- if (!data.active || data.paused)
- {
- return;
- }
-
- data.elapsed += data.unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
-
- data.elapsed = Mathf.Min(data.elapsed, data.duration);
-
- AssignMetrics(flow, data);
-
- var stack = flow.PreserveStack();
-
- flow.Invoke(tick);
-
- if (data.elapsed >= data.duration)
- {
- data.active = false;
-
- flow.RestoreStack(stack);
-
- flow.Invoke(completed);
- }
-
- flow.DisposePreservedStack(stack);
- }
- }
- }
|