Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,234 @@
using System;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Runs a cooldown timer to throttle flow and outputs remaining measurements.
/// </summary>
[UnitCategory("Time")]
[TypeIcon(typeof(Timer))]
[UnitOrder(8)]
public sealed class Cooldown : Unit, IGraphElementWithData, IGraphEventListener
{
public sealed class Data : IGraphElementData
{
public float remaining;
public float duration;
public bool unscaled;
public bool isReady => remaining <= 0;
public Delegate update;
public bool isListening;
}
/// <summary>
/// The moment at which to try using the cooldown.
/// </summary>
[DoNotSerialize]
[PortLabelHidden]
public ControlInput enter { get; private set; }
/// <summary>
/// Trigger to force reset the cooldown.
/// </summary>
[DoNotSerialize]
public ControlInput reset { get; private set; }
/// <summary>
/// The total duration of the cooldown.
/// </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 upon entry when the cooldown is ready.
/// </summary>
[DoNotSerialize]
[PortLabel("Ready")]
public ControlOutput exitReady { get; private set; }
/// <summary>
/// Called upon entry when the cooldown is not yet ready.
/// </summary>
[DoNotSerialize]
[PortLabel("Not Ready")]
public ControlOutput exitNotReady { get; private set; }
/// <summary>
/// Called each frame while the cooldown timer is active.
/// </summary>
[DoNotSerialize]
public ControlOutput tick { get; private set; }
/// <summary>
/// Called when the cooldown timer reaches zero.
/// </summary>
[DoNotSerialize]
[PortLabel("Completed")]
public ControlOutput becameReady { get; private set; }
/// <summary>
/// The number of seconds remaining until the cooldown is ready.
/// </summary>
[DoNotSerialize]
[PortLabel("Remaining")]
public ValueOutput remainingSeconds { get; private set; }
/// <summary>
/// The proportion of the duration remaining until the cooldown is ready (0-1).
/// </summary>
[DoNotSerialize]
[PortLabel("Remaining %")]
public ValueOutput remainingRatio { get; private set; }
protected override void Definition()
{
enter = ControlInput(nameof(enter), Enter);
reset = ControlInput(nameof(reset), Reset);
duration = ValueInput(nameof(duration), 1f);
unscaledTime = ValueInput(nameof(unscaledTime), false);
exitReady = ControlOutput(nameof(exitReady));
exitNotReady = ControlOutput(nameof(exitNotReady));
tick = ControlOutput(nameof(tick));
becameReady = ControlOutput(nameof(becameReady));
remainingSeconds = ValueOutput<float>(nameof(remainingSeconds));
remainingRatio = ValueOutput<float>(nameof(remainingRatio));
Requirement(duration, enter);
Requirement(unscaledTime, enter);
Succession(enter, exitReady);
Succession(enter, exitNotReady);
Succession(enter, tick);
Succession(enter, becameReady);
Assignment(enter, remainingSeconds);
Assignment(enter, 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 Enter(Flow flow)
{
var data = flow.stack.GetElementData<Data>(this);
if (data.isReady)
{
Reset(flow);
return exitReady;
}
else
{
return exitNotReady;
}
}
private ControlOutput Reset(Flow flow)
{
var data = flow.stack.GetElementData<Data>(this);
data.duration = flow.GetValue<float>(duration);
data.remaining = data.duration;
data.unscaled = flow.GetValue<bool>(unscaledTime);
return null;
}
private void AssignMetrics(Flow flow, Data data)
{
flow.SetValue(remainingSeconds, data.remaining);
flow.SetValue(remainingRatio, Mathf.Clamp01(data.remaining / data.duration));
}
public void Update(Flow flow)
{
var data = flow.stack.GetElementData<Data>(this);
if (data.isReady)
{
return;
}
data.remaining -= data.unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
data.remaining = Mathf.Max(0f, data.remaining);
AssignMetrics(flow, data);
var stack = flow.PreserveStack();
flow.Invoke(tick);
if (data.isReady)
{
flow.RestoreStack(stack);
flow.Invoke(becameReady);
}
flow.DisposePreservedStack(stack);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 962d2c1de100b4bee951cc5d53693b9f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,280 @@
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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97745dc9f2f4640559e8bd02331d9feb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
using System.Collections;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Delays flow by waiting until the end of the frame.
/// </summary>
[UnitTitle("Wait For End of Frame")]
[UnitShortTitle("End of Frame")]
[UnitOrder(5)]
public class WaitForEndOfFrameUnit : WaitUnit
{
protected override IEnumerator Await(Flow flow)
{
yield return new WaitForEndOfFrame();
yield return exit;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08418bca4bc8e4506aeb510a5267664a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,162 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Delays flow by waiting until multiple input flows have been executed.
/// </summary>
[UnitCategory("Time")]
[UnitOrder(6)]
[TypeIcon(typeof(WaitUnit))]
public sealed class WaitForFlow : Unit, IGraphElementWithData
{
public sealed class Data : IGraphElementData
{
public bool[] inputsActivated;
public bool isWaitingCoroutine;
}
/// <summary>
/// Whether the activation status should be reset on exit.
/// </summary>
[Serialize]
[Inspectable]
public bool resetOnExit { get; set; }
[SerializeAs(nameof(inputCount))]
private int _inputCount = 2;
[DoNotSerialize]
[Inspectable, UnitHeaderInspectable("Inputs")]
public int inputCount
{
get => _inputCount;
set => _inputCount = Mathf.Clamp(value, 2, 10);
}
[DoNotSerialize]
public ReadOnlyCollection<ControlInput> awaitedInputs { get; private set; }
/// <summary>
/// Trigger to reset the activation status.
/// </summary>
[DoNotSerialize]
public ControlInput reset { get; private set; }
/// <summary>
/// Triggered after all inputs have been entered at least once.
/// </summary>
[DoNotSerialize]
[PortLabelHidden]
public ControlOutput exit { get; private set; }
protected override void Definition()
{
var _awaitedInputs = new List<ControlInput>();
awaitedInputs = _awaitedInputs.AsReadOnly();
exit = ControlOutput(nameof(exit));
for (var i = 0; i < inputCount; i++)
{
var _i = i; // Cache outside closure
var awaitedInput = ControlInputCoroutine(_i.ToString(), (flow) => Enter(flow, _i), (flow) => EnterCoroutine(flow, _i));
_awaitedInputs.Add(awaitedInput);
Succession(awaitedInput, exit);
}
reset = ControlInput(nameof(reset), Reset);
}
public IGraphElementData CreateData()
{
return new Data() { inputsActivated = new bool[inputCount] };
}
private ControlOutput Enter(Flow flow, int index)
{
var data = flow.stack.GetElementData<Data>(this);
data.inputsActivated[index] = true;
if (CheckActivated(flow))
{
if (resetOnExit)
{
Reset(flow);
}
return exit;
}
else
{
return null;
}
}
private bool CheckActivated(Flow flow)
{
var data = flow.stack.GetElementData<Data>(this);
for (int i = 0; i < data.inputsActivated.Length; i++)
{
if (!data.inputsActivated[i])
{
return false;
}
}
return true;
}
private IEnumerator EnterCoroutine(Flow flow, int index)
{
var data = flow.stack.GetElementData<Data>(this);
data.inputsActivated[index] = true;
if (data.isWaitingCoroutine)
{
// Another input started an async wait,
// we'll let that flow be responsible for
// triggering the exit.
yield break;
}
if (!CheckActivated(flow))
{
data.isWaitingCoroutine = true;
yield return new WaitUntil(() => CheckActivated(flow));
data.isWaitingCoroutine = false;
}
if (resetOnExit)
{
Reset(flow);
}
yield return exit;
}
private ControlOutput Reset(Flow flow)
{
var data = flow.stack.GetElementData<Data>(this);
for (int i = 0; i < data.inputsActivated.Length; i++)
{
data.inputsActivated[i] = false;
}
return null;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f1ce51feaceca405daca97b5a2789be9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,20 @@
using System.Collections;
namespace Unity.VisualScripting
{
/// <summary>
/// Delays flow by waiting until the next frame.
/// </summary>
[UnitTitle("Wait For Next Frame")]
[UnitShortTitle("Next Frame")]
[UnitOrder(4)]
public class WaitForNextFrameUnit : WaitUnit
{
protected override IEnumerator Await(Flow flow)
{
yield return null;
yield return exit;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e479a8edc294743d0850fde55adafead
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,55 @@
using System.Collections;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Delays flow by waiting a specified number of seconds.
/// </summary>
[UnitTitle("Wait For Seconds")]
[UnitShortTitle("Wait")]
[UnitOrder(1)]
public class WaitForSecondsUnit : WaitUnit
{
/// <summary>
/// The number of seconds to await.
/// </summary>
[DoNotSerialize]
[PortLabel("Delay")]
public ValueInput seconds { get; private set; }
/// <summary>
/// Whether to ignore the time scale.
/// </summary>
[DoNotSerialize]
[PortLabel("Unscaled")]
public ValueInput unscaledTime { get; private set; }
protected override void Definition()
{
base.Definition();
seconds = ValueInput(nameof(seconds), 0f);
unscaledTime = ValueInput(nameof(unscaledTime), false);
Requirement(seconds, enter);
Requirement(unscaledTime, enter);
}
protected override IEnumerator Await(Flow flow)
{
var seconds = flow.GetValue<float>(this.seconds);
if (flow.GetValue<bool>(unscaledTime))
{
yield return new WaitForSecondsRealtime(seconds);
}
else
{
yield return new WaitForSeconds(seconds);
}
yield return exit;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 864067c09f11046dba76f5cacba7cbba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using System.Collections;
namespace Unity.VisualScripting
{
[UnitCategory("Time")]
public abstract class WaitUnit : Unit
{
/// <summary>
/// The moment at which to start the delay.
/// </summary>
[DoNotSerialize]
[PortLabelHidden]
public ControlInput enter { get; private set; }
/// <summary>
/// The action to execute after the delay has elapsed.
/// </summary>
[DoNotSerialize]
[PortLabelHidden]
public ControlOutput exit { get; private set; }
protected override void Definition()
{
enter = ControlInputCoroutine(nameof(enter), Await);
exit = ControlOutput(nameof(exit));
Succession(enter, exit);
}
protected abstract IEnumerator Await(Flow flow);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cc6b78093d57e40f883de7672944949a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,35 @@
using System.Collections;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Delays flow by waiting until a condition becomes true.
/// </summary>
[UnitTitle("Wait Until")]
[UnitShortTitle("Wait Until")]
[UnitOrder(2)]
public class WaitUntilUnit : WaitUnit
{
/// <summary>
/// The condition to await.
/// </summary>
[DoNotSerialize]
public ValueInput condition { get; private set; }
protected override void Definition()
{
base.Definition();
condition = ValueInput<bool>(nameof(condition));
Requirement(condition, enter);
}
protected override IEnumerator Await(Flow flow)
{
yield return new WaitUntil(() => flow.GetValue<bool>(condition));
yield return exit;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: edb411d5c9d5e4a40a66972455d3f334
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,35 @@
using System.Collections;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Delays flow by waiting while a condition is true.
/// </summary>
[UnitTitle("Wait While")]
[UnitShortTitle("Wait While")]
[UnitOrder(3)]
public class WaitWhileUnit : WaitUnit
{
/// <summary>
/// The condition to check.
/// </summary>
[DoNotSerialize]
public ValueInput condition { get; private set; }
protected override void Definition()
{
base.Definition();
condition = ValueInput<bool>(nameof(condition));
Requirement(condition, enter);
}
protected override IEnumerator Await(Flow flow)
{
yield return new WaitWhile(() => flow.GetValue<bool>(condition));
yield return exit;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a5a5df0e4f7041b691dcef865c9c428
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: