testss
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a struct with its default initializer.
|
||||
/// </summary>
|
||||
[SpecialUnit]
|
||||
public sealed class CreateStruct : Unit
|
||||
{
|
||||
[Obsolete(Serialization.ConstructorWarning)]
|
||||
public CreateStruct() : base() { }
|
||||
|
||||
public CreateStruct(Type type) : base()
|
||||
{
|
||||
Ensure.That(nameof(type)).IsNotNull(type);
|
||||
|
||||
if (!type.IsStruct())
|
||||
{
|
||||
throw new ArgumentException($"Type {type} must be a struct.", nameof(type));
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
[Serialize]
|
||||
public Type type { get; internal set; }
|
||||
|
||||
// Shouldn't happen through normal use, but can happen
|
||||
// if deserialization fails to find the type
|
||||
// https://support.ludiq.io/communities/5/topics/1661-x
|
||||
public override bool canDefine => type != null;
|
||||
|
||||
/// <summary>
|
||||
/// The entry point to create the struct. You can
|
||||
/// still get the return value without connecting this port.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to call once the struct has been created.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The created struct.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput output { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
exit = ControlOutput(nameof(exit));
|
||||
output = ValueOutput(type, nameof(output), Create);
|
||||
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
private ControlOutput Enter(Flow flow)
|
||||
{
|
||||
flow.SetValue(output, Activator.CreateInstance(type));
|
||||
|
||||
return exit;
|
||||
}
|
||||
|
||||
private object Create(Flow flow)
|
||||
{
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes all members of the type.
|
||||
/// </summary>
|
||||
[SpecialUnit]
|
||||
public sealed class Expose : Unit, IAotStubbable
|
||||
{
|
||||
public Expose() : base() { }
|
||||
|
||||
public Expose(Type type) : base()
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
[Serialize, Inspectable, TypeFilter(Enums = false)]
|
||||
public Type type { get; set; }
|
||||
|
||||
[Serialize, Inspectable, UnitHeaderInspectable("Instance"), InspectorToggleLeft]
|
||||
public bool instance { get; set; } = true;
|
||||
|
||||
[Serialize, Inspectable, UnitHeaderInspectable("Static"), InspectorToggleLeft]
|
||||
public bool @static { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The instance of the exposed type.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
[NullMeansSelf]
|
||||
public ValueInput target { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public Dictionary<ValueOutput, Member> members { get; private set; }
|
||||
|
||||
public override bool canDefine => type != null;
|
||||
|
||||
IEnumerable<object> IAotStubbable.aotStubs
|
||||
{
|
||||
get
|
||||
{
|
||||
if (members != null)
|
||||
{
|
||||
foreach (var member in members.Values)
|
||||
{
|
||||
if (member != null && member.isReflected)
|
||||
{
|
||||
yield return member.info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
members = new Dictionary<ValueOutput, Member>();
|
||||
|
||||
var requiresTarget = false;
|
||||
|
||||
foreach (var member in type.GetMembers()
|
||||
.Where(m => m is FieldInfo || m is PropertyInfo)
|
||||
.Select(m => m.ToManipulator(type))
|
||||
.DistinctBy(m => m.name) // To account for "new" duplicates
|
||||
.Where(Include)
|
||||
.OrderBy(m => m.requiresTarget ? 0 : 1)
|
||||
.ThenBy(m => m.order))
|
||||
{
|
||||
var memberPort = ValueOutput(member.type, member.name, (flow) => GetValue(flow, member));
|
||||
|
||||
if (member.isPredictable)
|
||||
{
|
||||
memberPort.Predictable();
|
||||
}
|
||||
|
||||
members.Add(memberPort, member);
|
||||
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
requiresTarget = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (requiresTarget)
|
||||
{
|
||||
target = ValueInput(type, nameof(target)).NullMeansSelf();
|
||||
|
||||
target.SetDefaultValue(type.PseudoDefault());
|
||||
|
||||
foreach (var member in members.Keys)
|
||||
{
|
||||
if (members[member].requiresTarget)
|
||||
{
|
||||
Requirement(target, member);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool Include(Member member)
|
||||
{
|
||||
if (!instance && member.requiresTarget)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!@static && !member.requiresTarget)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!member.isPubliclyGettable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (member.info.HasAttribute<ObsoleteAttribute>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (member.isIndexer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pesky edit-mode only accessor that is only available in the editor,
|
||||
// yet isn't marked by any special attribute to indicate it.
|
||||
if (member.name == "runInEditMode" && member.declaringType == typeof(MonoBehaviour))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private object GetValue(Flow flow, Member member)
|
||||
{
|
||||
var target = member.requiresTarget ? flow.GetValue(this.target, member.targetType) : null;
|
||||
|
||||
return member.Get(target);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value of a field or property via reflection.
|
||||
/// </summary>
|
||||
public sealed class GetMember : MemberUnit
|
||||
{
|
||||
public GetMember() { }
|
||||
|
||||
public GetMember(Member member) : base(member) { }
|
||||
|
||||
[DoNotSerialize]
|
||||
[MemberFilter(Fields = true, Properties = true, WriteOnly = false)]
|
||||
public Member getter
|
||||
{
|
||||
get
|
||||
{
|
||||
return member;
|
||||
}
|
||||
set
|
||||
{
|
||||
member = value;
|
||||
}
|
||||
}
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
value = ValueOutput(member.type, nameof(value), Value);
|
||||
|
||||
if (member.isPredictable)
|
||||
{
|
||||
value.Predictable();
|
||||
}
|
||||
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
Requirement(target, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsMemberValid(Member member)
|
||||
{
|
||||
return member.isAccessor && member.isGettable;
|
||||
}
|
||||
|
||||
private object Value(Flow flow)
|
||||
{
|
||||
var target = member.requiresTarget ? flow.GetValue(this.target, member.targetType) : null;
|
||||
|
||||
return member.Get(target);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Invokes a method or a constructor via reflection.
|
||||
/// </summary>
|
||||
public sealed class InvokeMember : MemberUnit
|
||||
{
|
||||
public InvokeMember() : base() { }
|
||||
|
||||
public InvokeMember(Member member) : base(member) { }
|
||||
|
||||
private bool useExpandedParameters;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the target should be output to allow for chaining.
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
[InspectableIf(nameof(supportsChaining))]
|
||||
public bool chainable { get; set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public bool supportsChaining => member.requiresTarget;
|
||||
|
||||
[DoNotSerialize]
|
||||
[MemberFilter(Methods = true, Constructors = true)]
|
||||
public Member invocation
|
||||
{
|
||||
get { return member; }
|
||||
set { member = value; }
|
||||
}
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public Dictionary<int, ValueInput> inputParameters { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The target object used when setting the value.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Target")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput targetOutput { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput result { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public Dictionary<int, ValueOutput> outputParameters { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
private int parameterCount;
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
inputParameters = new Dictionary<int, ValueInput>();
|
||||
outputParameters = new Dictionary<int, ValueOutput>();
|
||||
useExpandedParameters = true;
|
||||
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
exit = ControlOutput(nameof(exit));
|
||||
Succession(enter, exit);
|
||||
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
Requirement(target, enter);
|
||||
}
|
||||
|
||||
if (supportsChaining && chainable)
|
||||
{
|
||||
targetOutput = ValueOutput(member.targetType, nameof(targetOutput));
|
||||
Assignment(enter, targetOutput);
|
||||
}
|
||||
|
||||
if (member.isGettable)
|
||||
{
|
||||
result = ValueOutput(member.type, nameof(result), Result);
|
||||
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
Requirement(target, result);
|
||||
}
|
||||
}
|
||||
|
||||
var parameterInfos = member.GetParameterInfos().ToArray();
|
||||
|
||||
parameterCount = parameterInfos.Length;
|
||||
|
||||
for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
|
||||
{
|
||||
var parameterInfo = parameterInfos[parameterIndex];
|
||||
|
||||
var parameterType = parameterInfo.UnderlyingParameterType();
|
||||
|
||||
if (!parameterInfo.HasOutModifier())
|
||||
{
|
||||
var inputParameterKey = "%" + parameterInfo.Name;
|
||||
|
||||
var inputParameter = ValueInput(parameterType, inputParameterKey);
|
||||
|
||||
inputParameters.Add(parameterIndex, inputParameter);
|
||||
|
||||
inputParameter.SetDefaultValue(parameterInfo.PseudoDefaultValue());
|
||||
|
||||
if (parameterInfo.AllowsNull())
|
||||
{
|
||||
inputParameter.AllowsNull();
|
||||
}
|
||||
|
||||
Requirement(inputParameter, enter);
|
||||
|
||||
if (member.isGettable)
|
||||
{
|
||||
Requirement(inputParameter, result);
|
||||
}
|
||||
}
|
||||
|
||||
if (parameterInfo.ParameterType.IsByRef || parameterInfo.IsOut)
|
||||
{
|
||||
var outputParameterKey = "&" + parameterInfo.Name;
|
||||
|
||||
var outputParameter = ValueOutput(parameterType, outputParameterKey);
|
||||
|
||||
outputParameters.Add(parameterIndex, outputParameter);
|
||||
|
||||
Assignment(enter, outputParameter);
|
||||
|
||||
useExpandedParameters = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputParameters.Count > 5)
|
||||
{
|
||||
useExpandedParameters = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsMemberValid(Member member)
|
||||
{
|
||||
return member.isInvocable;
|
||||
}
|
||||
|
||||
private object Invoke(object target, Flow flow)
|
||||
{
|
||||
if (useExpandedParameters)
|
||||
{
|
||||
switch (inputParameters.Count)
|
||||
{
|
||||
case 0:
|
||||
|
||||
return member.Invoke(target);
|
||||
|
||||
case 1:
|
||||
|
||||
return member.Invoke(target,
|
||||
flow.GetConvertedValue(inputParameters[0]));
|
||||
|
||||
case 2:
|
||||
|
||||
return member.Invoke(target,
|
||||
flow.GetConvertedValue(inputParameters[0]),
|
||||
flow.GetConvertedValue(inputParameters[1]));
|
||||
|
||||
case 3:
|
||||
|
||||
return member.Invoke(target,
|
||||
flow.GetConvertedValue(inputParameters[0]),
|
||||
flow.GetConvertedValue(inputParameters[1]),
|
||||
flow.GetConvertedValue(inputParameters[2]));
|
||||
|
||||
case 4:
|
||||
|
||||
return member.Invoke(target,
|
||||
flow.GetConvertedValue(inputParameters[0]),
|
||||
flow.GetConvertedValue(inputParameters[1]),
|
||||
flow.GetConvertedValue(inputParameters[2]),
|
||||
flow.GetConvertedValue(inputParameters[3]));
|
||||
|
||||
case 5:
|
||||
|
||||
return member.Invoke(target,
|
||||
flow.GetConvertedValue(inputParameters[0]),
|
||||
flow.GetConvertedValue(inputParameters[1]),
|
||||
flow.GetConvertedValue(inputParameters[2]),
|
||||
flow.GetConvertedValue(inputParameters[3]),
|
||||
flow.GetConvertedValue(inputParameters[4]));
|
||||
|
||||
default:
|
||||
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var arguments = new object[parameterCount];
|
||||
|
||||
for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
|
||||
{
|
||||
if (inputParameters.TryGetValue(parameterIndex, out var inputParameter))
|
||||
{
|
||||
arguments[parameterIndex] = flow.GetConvertedValue(inputParameter);
|
||||
}
|
||||
}
|
||||
|
||||
var result = member.Invoke(target, arguments);
|
||||
|
||||
for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
|
||||
{
|
||||
if (outputParameters.TryGetValue(parameterIndex, out var outputParameter))
|
||||
{
|
||||
flow.SetValue(outputParameter, arguments[parameterIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private object GetAndChainTarget(Flow flow)
|
||||
{
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
var target = flow.GetValue(this.target, member.targetType);
|
||||
|
||||
if (supportsChaining && chainable)
|
||||
{
|
||||
flow.SetValue(targetOutput, target);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private object Result(Flow flow)
|
||||
{
|
||||
var target = GetAndChainTarget(flow);
|
||||
|
||||
return Invoke(target, flow);
|
||||
}
|
||||
|
||||
private ControlOutput Enter(Flow flow)
|
||||
{
|
||||
var target = GetAndChainTarget(flow);
|
||||
|
||||
var result = Invoke(target, flow);
|
||||
|
||||
if (this.result != null)
|
||||
{
|
||||
flow.SetValue(this.result, result);
|
||||
}
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[SpecialUnit]
|
||||
public abstract class MemberUnit : Unit, IAotStubbable
|
||||
{
|
||||
protected MemberUnit() : base() { }
|
||||
|
||||
protected MemberUnit(Member member) : this()
|
||||
{
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
[Serialize]
|
||||
[MemberFilter(Fields = true, Properties = true, Methods = true, Constructors = true)]
|
||||
public Member member { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The target object.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
[NullMeansSelf]
|
||||
public ValueInput target { get; private set; }
|
||||
|
||||
public override bool canDefine => member != null;
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
member.EnsureReflected();
|
||||
|
||||
if (!IsMemberValid(member))
|
||||
{
|
||||
throw new NotSupportedException("The member type is not valid for this unit.");
|
||||
}
|
||||
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
target = ValueInput(member.targetType, nameof(target));
|
||||
|
||||
target.SetDefaultValue(member.targetType.PseudoDefault());
|
||||
|
||||
if (typeof(UnityObject).IsAssignableFrom(member.targetType))
|
||||
{
|
||||
target.NullMeansSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool IsMemberValid(Member member);
|
||||
|
||||
public override void Prewarm()
|
||||
{
|
||||
if (member != null && member.isReflected)
|
||||
{
|
||||
member.Prewarm();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<object> IAotStubbable.aotStubs
|
||||
{
|
||||
get
|
||||
{
|
||||
if (member != null && member.isReflected)
|
||||
{
|
||||
yield return member.info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the value of a field or property via reflection.
|
||||
/// </summary>
|
||||
public sealed class SetMember : MemberUnit
|
||||
{
|
||||
public SetMember() : base() { }
|
||||
|
||||
public SetMember(Member member) : base(member) { }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the target should be output to allow for chaining.
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
[InspectableIf(nameof(supportsChaining))]
|
||||
public bool chainable { get; set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public bool supportsChaining => member.requiresTarget;
|
||||
|
||||
[DoNotSerialize]
|
||||
[MemberFilter(Fields = true, Properties = true, ReadOnly = false)]
|
||||
public Member setter
|
||||
{
|
||||
get => member;
|
||||
set => member = value;
|
||||
}
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput assign { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Value")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput input { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Value")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput output { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The target object used when setting the value.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Target")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput targetOutput { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput assigned { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
assign = ControlInput(nameof(assign), Assign);
|
||||
assigned = ControlOutput(nameof(assigned));
|
||||
Succession(assign, assigned);
|
||||
|
||||
if (supportsChaining && chainable)
|
||||
{
|
||||
targetOutput = ValueOutput(member.targetType, nameof(targetOutput));
|
||||
Assignment(assign, targetOutput);
|
||||
}
|
||||
|
||||
output = ValueOutput(member.type, nameof(output));
|
||||
Assignment(assign, output);
|
||||
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
Requirement(target, assign);
|
||||
}
|
||||
|
||||
input = ValueInput(member.type, nameof(input));
|
||||
Requirement(input, assign);
|
||||
|
||||
if (member.allowsNull)
|
||||
{
|
||||
input.AllowsNull();
|
||||
}
|
||||
|
||||
input.SetDefaultValue(member.type.PseudoDefault());
|
||||
}
|
||||
|
||||
protected override bool IsMemberValid(Member member)
|
||||
{
|
||||
return member.isAccessor && member.isSettable;
|
||||
}
|
||||
|
||||
private object GetAndChainTarget(Flow flow)
|
||||
{
|
||||
if (member.requiresTarget)
|
||||
{
|
||||
var target = flow.GetValue(this.target, member.targetType);
|
||||
|
||||
if (supportsChaining && chainable)
|
||||
{
|
||||
flow.SetValue(targetOutput, target);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ControlOutput Assign(Flow flow)
|
||||
{
|
||||
var target = GetAndChainTarget(flow);
|
||||
|
||||
var value = flow.GetConvertedValue(input);
|
||||
|
||||
flow.SetValue(output, member.Set(target, value));
|
||||
|
||||
return assigned;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Counts all items in a collection or enumeration.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections")]
|
||||
public sealed class CountItems : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The collection.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput collection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of items contained in the collection.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput count { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
collection = ValueInput<IEnumerable>(nameof(collection));
|
||||
count = ValueOutput(nameof(count), Count);
|
||||
|
||||
Requirement(collection, count);
|
||||
}
|
||||
|
||||
public int Count(Flow flow)
|
||||
{
|
||||
var enumerable = flow.GetValue<IEnumerable>(collection);
|
||||
|
||||
if (enumerable is ICollection)
|
||||
{
|
||||
return ((ICollection)enumerable).Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
return enumerable.Cast<object>().Count();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds an item to a dictionary.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitSurtitle("Dictionary")]
|
||||
[UnitShortTitle("Add Item")]
|
||||
[UnitOrder(2)]
|
||||
public sealed class AddDictionaryItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Dictionary")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput dictionaryInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dictionary with the added element.
|
||||
/// Note that the input dictionary is modified directly then returned.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Dictionary")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput dictionaryOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the item to add.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the item to add.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been added.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Add);
|
||||
dictionaryInput = ValueInput<IDictionary>(nameof(dictionaryInput));
|
||||
key = ValueInput<object>(nameof(key));
|
||||
value = ValueInput<object>(nameof(value));
|
||||
dictionaryOutput = ValueOutput<IDictionary>(nameof(dictionaryOutput));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(dictionaryInput, enter);
|
||||
Requirement(key, enter);
|
||||
Requirement(value, enter);
|
||||
Assignment(enter, dictionaryOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
private ControlOutput Add(Flow flow)
|
||||
{
|
||||
var dictionary = flow.GetValue<IDictionary>(dictionaryInput);
|
||||
var key = flow.GetValue<object>(this.key);
|
||||
var value = flow.GetValue<object>(this.value);
|
||||
|
||||
flow.SetValue(dictionaryOutput, dictionary);
|
||||
|
||||
dictionary.Add(key, value);
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears all items from a dictionary.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitSurtitle("Dictionary")]
|
||||
[UnitShortTitle("Clear")]
|
||||
[UnitOrder(4)]
|
||||
[TypeIcon(typeof(RemoveDictionaryItem))]
|
||||
public sealed class ClearDictionary : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Dictionary")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput dictionaryInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The cleared dictionary.
|
||||
/// Note that the input dictionary is modified directly and then returned.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Dictionary")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput dictionaryOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the dictionary has been cleared.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Clear);
|
||||
dictionaryInput = ValueInput<IDictionary>(nameof(dictionaryInput));
|
||||
dictionaryOutput = ValueOutput<IDictionary>(nameof(dictionaryOutput));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(dictionaryInput, enter);
|
||||
Assignment(enter, dictionaryOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
private ControlOutput Clear(Flow flow)
|
||||
{
|
||||
var dictionary = flow.GetValue<IDictionary>(dictionaryInput);
|
||||
|
||||
flow.SetValue(dictionaryOutput, dictionary);
|
||||
|
||||
dictionary.Clear();
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an empty dictionary.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitOrder(-1)]
|
||||
[TypeIcon(typeof(IDictionary))]
|
||||
[RenamedFrom("Bolt.CreateDitionary")]
|
||||
public sealed class CreateDictionary : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The new empty dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput dictionary { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
dictionary = ValueOutput(nameof(dictionary), Create);
|
||||
}
|
||||
|
||||
public IDictionary Create(Flow flow)
|
||||
{
|
||||
return new AotDictionary();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks whether a dictionary contains the specified key.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitSurtitle("Dictionary")]
|
||||
[UnitShortTitle("Contains Key")]
|
||||
[TypeIcon(typeof(IDictionary))]
|
||||
public sealed class DictionaryContainsKey : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput dictionary { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the list contains the item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput contains { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
dictionary = ValueInput<IDictionary>(nameof(dictionary));
|
||||
key = ValueInput<object>(nameof(key));
|
||||
contains = ValueOutput(nameof(contains), Contains);
|
||||
|
||||
Requirement(dictionary, contains);
|
||||
Requirement(key, contains);
|
||||
}
|
||||
|
||||
private bool Contains(Flow flow)
|
||||
{
|
||||
var dictionary = flow.GetValue<IDictionary>(this.dictionary);
|
||||
var key = flow.GetValue<object>(this.key);
|
||||
|
||||
return dictionary.Contains(key);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a dictionary item with the specified key.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitSurtitle("Dictionary")]
|
||||
[UnitShortTitle("Get Item")]
|
||||
[UnitOrder(0)]
|
||||
[TypeIcon(typeof(IDictionary))]
|
||||
public sealed class GetDictionaryItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput dictionary { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
dictionary = ValueInput<IDictionary>(nameof(dictionary));
|
||||
key = ValueInput<object>(nameof(key));
|
||||
value = ValueOutput(nameof(value), Get);
|
||||
|
||||
Requirement(dictionary, value);
|
||||
Requirement(key, value);
|
||||
}
|
||||
|
||||
private object Get(Flow flow)
|
||||
{
|
||||
var dictionary = flow.GetValue<IDictionary>(this.dictionary);
|
||||
var key = flow.GetValue<object>(this.key);
|
||||
|
||||
return dictionary[key];
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Merges two or more dictionaries together.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the same key is found more than once, only the value
|
||||
/// of the first dictionary with this key will be used.
|
||||
/// </remarks>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitOrder(5)]
|
||||
public sealed class MergeDictionaries : MultiInputUnit<IDictionary>
|
||||
{
|
||||
/// <summary>
|
||||
/// The merged dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput dictionary { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
dictionary = ValueOutput(nameof(dictionary), Merge);
|
||||
|
||||
base.Definition();
|
||||
|
||||
foreach (var input in multiInputs)
|
||||
{
|
||||
Requirement(input, dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
public IDictionary Merge(Flow flow)
|
||||
{
|
||||
var dictionary = new AotDictionary();
|
||||
|
||||
for (var i = 0; i < inputCount; i++)
|
||||
{
|
||||
var inputDictionary = flow.GetValue<IDictionary>(multiInputs[i]);
|
||||
|
||||
var enumerator = inputDictionary.GetEnumerator();
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
if (!dictionary.Contains(enumerator.Key))
|
||||
{
|
||||
dictionary.Add(enumerator.Key, enumerator.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes a dictionary item with a specified key.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitSurtitle("Dictionary")]
|
||||
[UnitShortTitle("Remove Item")]
|
||||
[UnitOrder(3)]
|
||||
public sealed class RemoveDictionaryItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Dictionary")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput dictionaryInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dictionary without the removed item.
|
||||
/// Note that the input dictionary is modified directly and then returned.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Dictionary")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput dictionaryOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the item to remove.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been removed.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Remove);
|
||||
dictionaryInput = ValueInput<IDictionary>(nameof(dictionaryInput));
|
||||
dictionaryOutput = ValueOutput<IDictionary>(nameof(dictionaryOutput));
|
||||
key = ValueInput<object>(nameof(key));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(dictionaryInput, enter);
|
||||
Requirement(key, enter);
|
||||
Assignment(enter, dictionaryOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput Remove(Flow flow)
|
||||
{
|
||||
var dictionary = flow.GetValue<IDictionary>(dictionaryInput);
|
||||
var key = flow.GetValue<object>(this.key);
|
||||
|
||||
flow.SetValue(dictionaryOutput, dictionary);
|
||||
|
||||
dictionary.Remove(key);
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the value of a dictionary item with the specified key.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Dictionaries")]
|
||||
[UnitSurtitle("Dictionary")]
|
||||
[UnitShortTitle("Set Item")]
|
||||
[UnitOrder(1)]
|
||||
[TypeIcon(typeof(IDictionary))]
|
||||
public sealed class SetDictionaryItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dictionary.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput dictionary { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the item to set.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value to assign to the item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been assigned.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Set);
|
||||
dictionary = ValueInput<IDictionary>(nameof(dictionary));
|
||||
key = ValueInput<object>(nameof(key));
|
||||
value = ValueInput<object>(nameof(value));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(dictionary, enter);
|
||||
Requirement(key, enter);
|
||||
Requirement(value, enter);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput Set(Flow flow)
|
||||
{
|
||||
var dictionary = flow.GetValue<IDictionary>(this.dictionary);
|
||||
var key = flow.GetValue<object>(this.key);
|
||||
var value = flow.GetValue<object>(this.value);
|
||||
|
||||
dictionary[key] = value;
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the first item in a collection or enumeration.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections")]
|
||||
public sealed class FirstItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The collection.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput collection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The first item of the collection.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput firstItem { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
collection = ValueInput<IEnumerable>(nameof(collection));
|
||||
firstItem = ValueOutput(nameof(firstItem), First);
|
||||
|
||||
Requirement(collection, firstItem);
|
||||
}
|
||||
|
||||
public object First(Flow flow)
|
||||
{
|
||||
var enumerable = flow.GetValue<IEnumerable>(collection);
|
||||
|
||||
if (enumerable is IList)
|
||||
{
|
||||
return ((IList)enumerable)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return enumerable.Cast<object>().First();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the first item in a collection or enumeration.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections")]
|
||||
public sealed class LastItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The collection.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput collection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The last item of the collection.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput lastItem { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
collection = ValueInput<IEnumerable>(nameof(collection));
|
||||
lastItem = ValueOutput(nameof(lastItem), First);
|
||||
|
||||
Requirement(collection, lastItem);
|
||||
}
|
||||
|
||||
public object First(Flow flow)
|
||||
{
|
||||
var enumerable = flow.GetValue<IEnumerable>(collection);
|
||||
|
||||
if (enumerable is IList)
|
||||
{
|
||||
var list = (IList)enumerable;
|
||||
|
||||
return list[list.Count - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return enumerable.Cast<object>().Last();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds an item to a list.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Add Item")]
|
||||
[UnitOrder(2)]
|
||||
public sealed class AddListItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput listInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list with the added element.
|
||||
/// Note that the input list is modified directly and then returned,
|
||||
/// except if it is an array, in which case a new array with
|
||||
/// the added element is returned instead.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput listOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The item to add.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput item { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been added.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Add);
|
||||
listInput = ValueInput<IList>(nameof(listInput));
|
||||
item = ValueInput<object>(nameof(item));
|
||||
listOutput = ValueOutput<IList>(nameof(listOutput));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(listInput, enter);
|
||||
Requirement(item, enter);
|
||||
Assignment(enter, listOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput Add(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(listInput);
|
||||
var item = flow.GetValue<object>(this.item);
|
||||
|
||||
if (list is Array)
|
||||
{
|
||||
var resizableList = new ArrayList(list);
|
||||
resizableList.Add(item);
|
||||
flow.SetValue(listOutput, resizableList.ToArray(list.GetType().GetElementType()));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(item);
|
||||
|
||||
flow.SetValue(listOutput, list);
|
||||
}
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears all items from a list.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Clear")]
|
||||
[UnitOrder(6)]
|
||||
[TypeIcon(typeof(RemoveListItem))]
|
||||
public sealed class ClearList : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput listInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The cleared list.
|
||||
/// Note that the input list is modified directly and then returned,
|
||||
/// except if it is an array, in which case a new empty array
|
||||
/// is returned instead.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput listOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the list has been cleared.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Clear);
|
||||
listInput = ValueInput<IList>(nameof(listInput));
|
||||
listOutput = ValueOutput<IList>(nameof(listOutput));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(listInput, enter);
|
||||
Assignment(enter, listOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput Clear(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(listInput);
|
||||
|
||||
if (list is Array)
|
||||
{
|
||||
flow.SetValue(listOutput, Array.CreateInstance(list.GetType().GetElementType(), 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
flow.SetValue(listOutput, list);
|
||||
}
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a list from a number of item inputs.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitOrder(-1)]
|
||||
[TypeIcon(typeof(IList))]
|
||||
public sealed class CreateList : MultiInputUnit<object>
|
||||
{
|
||||
[DoNotSerialize]
|
||||
protected override int minInputCount => 0;
|
||||
|
||||
[InspectorLabel("Elements")]
|
||||
[UnitHeaderInspectable("Elements")]
|
||||
[Inspectable]
|
||||
public override int inputCount
|
||||
{
|
||||
get => base.inputCount;
|
||||
set => base.inputCount = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The created list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput list { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
list = ValueOutput(nameof(list), Create);
|
||||
|
||||
base.Definition();
|
||||
|
||||
foreach (var input in multiInputs)
|
||||
{
|
||||
Requirement(input, list);
|
||||
}
|
||||
|
||||
InputsAllowNull();
|
||||
}
|
||||
|
||||
public IList Create(Flow flow)
|
||||
{
|
||||
var list = new AotList();
|
||||
|
||||
for (var i = 0; i < inputCount; i++)
|
||||
{
|
||||
list.Add(flow.GetValue<object>(multiInputs[i]));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the item at the specified index of a list.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Get Item")]
|
||||
[UnitOrder(0)]
|
||||
[TypeIcon(typeof(IList))]
|
||||
public sealed class GetListItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput list { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The zero-based index.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput index { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput item { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
list = ValueInput<IList>(nameof(list));
|
||||
index = ValueInput(nameof(index), 0);
|
||||
item = ValueOutput(nameof(item), Get);
|
||||
|
||||
Requirement(list, item);
|
||||
Requirement(index, item);
|
||||
}
|
||||
|
||||
public object Get(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(this.list);
|
||||
var index = flow.GetValue<int>(this.index);
|
||||
|
||||
return list[index];
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Inserts an item in a list at a specified index.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Insert Item")]
|
||||
[UnitOrder(3)]
|
||||
[TypeIcon(typeof(AddListItem))]
|
||||
public sealed class InsertListItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput listInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list with the added element.
|
||||
/// Note that the input list is modified directly and then returned,
|
||||
/// except if it is an array, in which case a new array with
|
||||
/// the added element is returned instead.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput listOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The zero-based index at which to insert the item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput index { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The item to insert.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput item { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been inserted.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Insert);
|
||||
listInput = ValueInput<IList>(nameof(listInput));
|
||||
item = ValueInput<object>(nameof(item));
|
||||
index = ValueInput(nameof(index), 0);
|
||||
listOutput = ValueOutput<IList>(nameof(listOutput));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(listInput, enter);
|
||||
Requirement(item, enter);
|
||||
Requirement(index, enter);
|
||||
Assignment(enter, listOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput Insert(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(listInput);
|
||||
var index = flow.GetValue<int>(this.index);
|
||||
var item = flow.GetValue<object>(this.item);
|
||||
|
||||
if (list is Array)
|
||||
{
|
||||
var resizableList = new ArrayList(list);
|
||||
resizableList.Insert(index, item);
|
||||
flow.SetValue(listOutput, resizableList.ToArray(list.GetType().GetElementType()));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Insert(index, item);
|
||||
flow.SetValue(listOutput, list);
|
||||
}
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks whether a list contains the specified item.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Contains Item")]
|
||||
[TypeIcon(typeof(IList))]
|
||||
public sealed class ListContainsItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput list { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput item { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the list contains the item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput contains { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
list = ValueInput<IList>(nameof(list));
|
||||
item = ValueInput<object>(nameof(item));
|
||||
contains = ValueOutput(nameof(contains), Contains);
|
||||
|
||||
Requirement(list, contains);
|
||||
Requirement(item, contains);
|
||||
}
|
||||
|
||||
public bool Contains(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(this.list);
|
||||
var item = flow.GetValue<object>(this.item);
|
||||
|
||||
return list.Contains(item);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Merges two or more lists together.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitOrder(7)]
|
||||
public sealed class MergeLists : MultiInputUnit<IEnumerable>
|
||||
{
|
||||
/// <summary>
|
||||
/// The merged list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput list { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
list = ValueOutput(nameof(list), Merge);
|
||||
|
||||
base.Definition();
|
||||
|
||||
foreach (var input in multiInputs)
|
||||
{
|
||||
Requirement(input, list);
|
||||
}
|
||||
}
|
||||
|
||||
public IList Merge(Flow flow)
|
||||
{
|
||||
var list = new AotList();
|
||||
|
||||
for (var i = 0; i < inputCount; i++)
|
||||
{
|
||||
list.AddRange(flow.GetValue<IEnumerable>(multiInputs[i]));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes the specified item from a list.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Remove Item")]
|
||||
[UnitOrder(4)]
|
||||
public sealed class RemoveListItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput listInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list without the removed item.
|
||||
/// Note that the input list is modified directly and then returned,
|
||||
/// except if it is an array, in which case a new array without the item
|
||||
/// is returned instead.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("List")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput listOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The item to remove.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput item { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been removed.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Remove);
|
||||
listInput = ValueInput<IList>(nameof(listInput));
|
||||
listOutput = ValueOutput<IList>(nameof(listOutput));
|
||||
item = ValueInput<object>(nameof(item));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(listInput, enter);
|
||||
Requirement(item, enter);
|
||||
Assignment(enter, listOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput Remove(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(listInput);
|
||||
var item = flow.GetValue<object>(this.item);
|
||||
|
||||
if (list is Array)
|
||||
{
|
||||
var resizableList = new ArrayList(list);
|
||||
resizableList.Remove(item);
|
||||
flow.SetValue(listOutput, resizableList.ToArray(list.GetType().GetElementType()));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Remove(item);
|
||||
flow.SetValue(listOutput, list);
|
||||
}
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes the item at the specified index of a list.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Remove Item At Index")]
|
||||
[UnitOrder(5)]
|
||||
[TypeIcon(typeof(RemoveListItem))]
|
||||
public sealed class RemoveListItemAt : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput listInput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list without the removed item.
|
||||
/// Note that the input list is modified directly and then returned,
|
||||
/// except if it is an array, in which case a new array without the item
|
||||
/// is returned instead.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput listOutput { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The zero-based index.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput index { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been removed.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), RemoveAt);
|
||||
listInput = ValueInput<IList>(nameof(listInput));
|
||||
listOutput = ValueOutput<IList>(nameof(listOutput));
|
||||
index = ValueInput(nameof(index), 0);
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(listInput, enter);
|
||||
Requirement(index, enter);
|
||||
Assignment(enter, listOutput);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput RemoveAt(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(listInput);
|
||||
var index = flow.GetValue<int>(this.index);
|
||||
|
||||
if (list is Array)
|
||||
{
|
||||
var resizableList = new ArrayList(list);
|
||||
resizableList.RemoveAt(index);
|
||||
flow.SetValue(listOutput, resizableList.ToArray(list.GetType().GetElementType()));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.RemoveAt(index);
|
||||
flow.SetValue(listOutput, list);
|
||||
}
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the item at the specified index of a list.
|
||||
/// </summary>
|
||||
[UnitCategory("Collections/Lists")]
|
||||
[UnitSurtitle("List")]
|
||||
[UnitShortTitle("Set Item")]
|
||||
[UnitOrder(1)]
|
||||
[TypeIcon(typeof(IList))]
|
||||
public sealed class SetListItem : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the node.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput list { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The zero-based index.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput index { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The item.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput item { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the item has been assigned.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Set);
|
||||
list = ValueInput<IList>(nameof(list));
|
||||
index = ValueInput(nameof(index), 0);
|
||||
item = ValueInput<object>(nameof(item));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(list, enter);
|
||||
Requirement(index, enter);
|
||||
Requirement(item, enter);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
public ControlOutput Set(Flow flow)
|
||||
{
|
||||
var list = flow.GetValue<IList>(this.list);
|
||||
var index = flow.GetValue<int>(this.index);
|
||||
var item = flow.GetValue<object>(this.item);
|
||||
|
||||
list[index] = item;
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Stops the execution of the current loop.
|
||||
/// </summary>
|
||||
[UnitTitle("Break Loop")]
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(13)]
|
||||
public class Break : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the break.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Operation);
|
||||
}
|
||||
|
||||
public ControlOutput Operation(Flow flow)
|
||||
{
|
||||
flow.BreakLoop();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Caches the input so that all nodes connected to the output
|
||||
/// retrieve the value only once.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(15)]
|
||||
public sealed class Cache : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The moment at which to cache the value.
|
||||
/// The output value will only get updated when this gets triggered.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value to cache when the node is entered.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput input { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached value, as it was the last time this node was entered.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Cached")]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput output { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute once the value has been cached.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Store);
|
||||
input = ValueInput<object>(nameof(input));
|
||||
output = ValueOutput<object>(nameof(output));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
Requirement(input, enter);
|
||||
Assignment(enter, output);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
private ControlOutput Store(Flow flow)
|
||||
{
|
||||
flow.SetValue(output, flow.GetValue(input));
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Loops between a first and last index at a specified step.
|
||||
/// </summary>
|
||||
[UnitTitle("For Loop")]
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(9)]
|
||||
public sealed class For : LoopUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// The index at which to start the loop (inclusive).
|
||||
/// </summary>
|
||||
[PortLabel("First")]
|
||||
[DoNotSerialize]
|
||||
public ValueInput firstIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The index at which to end the loop (exclusive).
|
||||
/// </summary>
|
||||
[PortLabel("Last")]
|
||||
[DoNotSerialize]
|
||||
public ValueInput lastIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value by which the index will be incremented (or decremented, if negative) after each loop.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput step { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current index of the loop.
|
||||
/// </summary>
|
||||
[PortLabel("Index")]
|
||||
[DoNotSerialize]
|
||||
public ValueOutput currentIndex { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
firstIndex = ValueInput(nameof(firstIndex), 0);
|
||||
lastIndex = ValueInput(nameof(lastIndex), 10);
|
||||
step = ValueInput(nameof(step), 1);
|
||||
currentIndex = ValueOutput<int>(nameof(currentIndex));
|
||||
base.Definition();
|
||||
|
||||
Requirement(firstIndex, enter);
|
||||
Requirement(lastIndex, enter);
|
||||
Requirement(step, enter);
|
||||
Assignment(enter, currentIndex);
|
||||
}
|
||||
|
||||
private int Start(Flow flow, out int currentIndex, out int lastIndex, out bool ascending)
|
||||
{
|
||||
var firstIndex = flow.GetValue<int>(this.firstIndex);
|
||||
lastIndex = flow.GetValue<int>(this.lastIndex);
|
||||
ascending = firstIndex <= lastIndex;
|
||||
currentIndex = firstIndex;
|
||||
flow.SetValue(this.currentIndex, currentIndex);
|
||||
|
||||
return flow.EnterLoop();
|
||||
}
|
||||
|
||||
private bool CanMoveNext(int currentIndex, int lastIndex, bool ascending)
|
||||
{
|
||||
if (ascending)
|
||||
{
|
||||
return currentIndex < lastIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return currentIndex > lastIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveNext(Flow flow, ref int currentIndex)
|
||||
{
|
||||
currentIndex += flow.GetValue<int>(step);
|
||||
flow.SetValue(this.currentIndex, currentIndex);
|
||||
}
|
||||
|
||||
protected override ControlOutput Loop(Flow flow)
|
||||
{
|
||||
var loop = Start(flow, out int currentIndex, out int lastIndex, out bool ascending);
|
||||
|
||||
if (!IsStepValueZero())
|
||||
{
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
while (flow.LoopIsNotBroken(loop) && CanMoveNext(currentIndex, lastIndex, ascending))
|
||||
{
|
||||
flow.Invoke(body);
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
|
||||
MoveNext(flow, ref currentIndex);
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
}
|
||||
|
||||
flow.ExitLoop(loop);
|
||||
|
||||
return exit;
|
||||
}
|
||||
|
||||
protected override IEnumerator LoopCoroutine(Flow flow)
|
||||
{
|
||||
var loop = Start(flow, out int currentIndex, out int lastIndex, out bool ascending);
|
||||
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
while (flow.LoopIsNotBroken(loop) && CanMoveNext(currentIndex, lastIndex, ascending))
|
||||
{
|
||||
yield return body;
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
|
||||
MoveNext(flow, ref currentIndex);
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
|
||||
flow.ExitLoop(loop);
|
||||
|
||||
yield return exit;
|
||||
}
|
||||
|
||||
public bool IsStepValueZero()
|
||||
{
|
||||
var isDefaultZero = !step.hasValidConnection && (int)defaultValues[step.key] == 0;
|
||||
var isConnectedToLiteralZero = false;
|
||||
|
||||
if (step.hasValidConnection && step.connection.source.unit is Literal literal)
|
||||
{
|
||||
if (Convert.ToInt32(literal.value) == 0)
|
||||
{
|
||||
isConnectedToLiteralZero = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isDefaultZero || isConnectedToLiteralZero;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Loops over each element of a collection.
|
||||
/// </summary>
|
||||
[UnitTitle("For Each Loop")]
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(10)]
|
||||
public class ForEach : LoopUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// The collection over which to loop.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput collection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current index of the loop.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Index")]
|
||||
public ValueOutput currentIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the current item of the loop.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Key")]
|
||||
public ValueOutput currentKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current item of the loop.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Item")]
|
||||
public ValueOutput currentItem { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
[Inspectable, UnitHeaderInspectable("Dictionary")]
|
||||
[InspectorToggleLeft]
|
||||
public bool dictionary { get; set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
if (dictionary)
|
||||
{
|
||||
collection = ValueInput<IDictionary>(nameof(collection));
|
||||
}
|
||||
else
|
||||
{
|
||||
collection = ValueInput<IEnumerable>(nameof(collection));
|
||||
}
|
||||
|
||||
currentIndex = ValueOutput<int>(nameof(currentIndex));
|
||||
|
||||
if (dictionary)
|
||||
{
|
||||
currentKey = ValueOutput<object>(nameof(currentKey));
|
||||
}
|
||||
|
||||
currentItem = ValueOutput<object>(nameof(currentItem));
|
||||
|
||||
Requirement(collection, enter);
|
||||
Assignment(enter, currentIndex);
|
||||
Assignment(enter, currentItem);
|
||||
|
||||
if (dictionary)
|
||||
{
|
||||
Assignment(enter, currentKey);
|
||||
}
|
||||
}
|
||||
|
||||
private int Start(Flow flow, out IEnumerator enumerator, out IDictionaryEnumerator dictionaryEnumerator, out int currentIndex)
|
||||
{
|
||||
if (dictionary)
|
||||
{
|
||||
dictionaryEnumerator = flow.GetValue<IDictionary>(collection).GetEnumerator();
|
||||
enumerator = dictionaryEnumerator;
|
||||
}
|
||||
else
|
||||
{
|
||||
enumerator = flow.GetValue<IEnumerable>(collection).GetEnumerator();
|
||||
dictionaryEnumerator = null;
|
||||
}
|
||||
|
||||
currentIndex = -1;
|
||||
|
||||
return flow.EnterLoop();
|
||||
}
|
||||
|
||||
private bool MoveNext(Flow flow, IEnumerator enumerator, IDictionaryEnumerator dictionaryEnumerator, ref int currentIndex)
|
||||
{
|
||||
var result = enumerator.MoveNext();
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (dictionary)
|
||||
{
|
||||
flow.SetValue(currentKey, dictionaryEnumerator.Key);
|
||||
flow.SetValue(currentItem, dictionaryEnumerator.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
flow.SetValue(currentItem, enumerator.Current);
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
|
||||
flow.SetValue(this.currentIndex, currentIndex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override ControlOutput Loop(Flow flow)
|
||||
{
|
||||
var loop = Start(flow, out var enumerator, out var dictionaryEnumerator, out var currentIndex);
|
||||
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
try
|
||||
{
|
||||
while (flow.LoopIsNotBroken(loop) && MoveNext(flow, enumerator, dictionaryEnumerator, ref currentIndex))
|
||||
{
|
||||
flow.Invoke(body);
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
(enumerator as IDisposable)?.Dispose();
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
|
||||
flow.ExitLoop(loop);
|
||||
|
||||
return exit;
|
||||
}
|
||||
|
||||
protected override IEnumerator LoopCoroutine(Flow flow)
|
||||
{
|
||||
var loop = Start(flow, out var enumerator, out var dictionaryEnumerator, out var currentIndex);
|
||||
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
try
|
||||
{
|
||||
while (flow.LoopIsNotBroken(loop) && MoveNext(flow, enumerator, dictionaryEnumerator, ref currentIndex))
|
||||
{
|
||||
yield return body;
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
(enumerator as IDisposable)?.Dispose();
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
|
||||
flow.ExitLoop(loop);
|
||||
|
||||
yield return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[TypeIconPriority]
|
||||
public interface IBranchUnit : IUnit
|
||||
{
|
||||
ControlInput enter { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[TypeIconPriority]
|
||||
public interface ISelectUnit : IUnit
|
||||
{
|
||||
ValueOutput selection { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Branches flow by checking if a condition is true or false.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(0)]
|
||||
[RenamedFrom("Bolt.Branch")]
|
||||
[RenamedFrom("Unity.VisualScripting.Branch")]
|
||||
public sealed class If : Unit, IBranchUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the branch.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The condition to check.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput condition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute if the condition is true.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("True")]
|
||||
public ControlOutput ifTrue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute if the condition is false.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("False")]
|
||||
public ControlOutput ifFalse { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
condition = ValueInput<bool>(nameof(condition));
|
||||
ifTrue = ControlOutput(nameof(ifTrue));
|
||||
ifFalse = ControlOutput(nameof(ifFalse));
|
||||
|
||||
Requirement(condition, enter);
|
||||
Succession(enter, ifTrue);
|
||||
Succession(enter, ifFalse);
|
||||
}
|
||||
|
||||
public ControlOutput Enter(Flow flow)
|
||||
{
|
||||
return flow.GetValue<bool>(condition) ? ifTrue : ifFalse;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class LoopUnit : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the loop.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute after the loop has been completed or broken.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute at each loop.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput body { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInputCoroutine(nameof(enter), Loop, LoopCoroutine);
|
||||
exit = ControlOutput(nameof(exit));
|
||||
body = ControlOutput(nameof(body));
|
||||
|
||||
Succession(enter, body);
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
protected abstract ControlOutput Loop(Flow flow);
|
||||
|
||||
protected abstract IEnumerator LoopCoroutine(Flow flow);
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes an action only once, and a different action afterwards.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(14)]
|
||||
public sealed class Once : Unit, IGraphElementWithData
|
||||
{
|
||||
public sealed class Data : IGraphElementData
|
||||
{
|
||||
public bool executed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entry point for the action.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to reset the once check.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlInput reset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute the first time the unit is entered.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput once { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute subsequently.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput after { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
reset = ControlInput(nameof(reset), Reset);
|
||||
once = ControlOutput(nameof(once));
|
||||
after = ControlOutput(nameof(after));
|
||||
|
||||
Succession(enter, once);
|
||||
Succession(enter, after);
|
||||
}
|
||||
|
||||
public IGraphElementData CreateData()
|
||||
{
|
||||
return new Data();
|
||||
}
|
||||
|
||||
public ControlOutput Enter(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
if (!data.executed)
|
||||
{
|
||||
data.executed = true;
|
||||
|
||||
return once;
|
||||
}
|
||||
else
|
||||
{
|
||||
return after;
|
||||
}
|
||||
}
|
||||
|
||||
public ControlOutput Reset(Flow flow)
|
||||
{
|
||||
flow.stack.GetElementData<Data>(this).executed = false;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Selects a value from a set by switching over an enum.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Select On Enum")]
|
||||
[UnitShortTitle("Select")]
|
||||
[UnitSubtitle("On Enum")]
|
||||
[UnitOrder(7)]
|
||||
[TypeIcon(typeof(ISelectUnit))]
|
||||
public sealed class SelectOnEnum : Unit, ISelectUnit
|
||||
{
|
||||
[DoNotSerialize]
|
||||
public Dictionary<object, ValueInput> branches { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value on which to select.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput selector { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The selected value.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput selection { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
[Inspectable, UnitHeaderInspectable]
|
||||
[TypeFilter(Enums = true, Classes = false, Interfaces = false, Structs = false, Primitives = false)]
|
||||
public Type enumType { get; set; }
|
||||
|
||||
public override bool canDefine => enumType != null && enumType.IsEnum;
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
branches = new Dictionary<object, ValueInput>();
|
||||
|
||||
selection = ValueOutput(nameof(selection), Branch).Predictable();
|
||||
|
||||
selector = ValueInput(enumType, nameof(selector));
|
||||
|
||||
Requirement(selector, selection);
|
||||
|
||||
foreach (var valueByName in EnumUtility.ValuesByNames(enumType))
|
||||
{
|
||||
var enumValue = valueByName.Value;
|
||||
|
||||
if (branches.ContainsKey(enumValue))
|
||||
continue;
|
||||
|
||||
var branch = ValueInput<object>("%" + valueByName.Key).AllowsNull();
|
||||
|
||||
branches.Add(enumValue, branch);
|
||||
|
||||
Requirement(branch, selection);
|
||||
}
|
||||
}
|
||||
|
||||
public object Branch(Flow flow)
|
||||
{
|
||||
var selector = flow.GetValue(this.selector, enumType);
|
||||
|
||||
return flow.GetValue(branches[selector]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Selects a value from a set by matching it with an input flow.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Select On Flow")]
|
||||
[UnitShortTitle("Select")]
|
||||
[UnitSubtitle("On Flow")]
|
||||
[UnitOrder(8)]
|
||||
[TypeIcon(typeof(ISelectUnit))]
|
||||
public sealed class SelectOnFlow : Unit, ISelectUnit
|
||||
{
|
||||
[SerializeAs(nameof(branchCount))]
|
||||
private int _branchCount = 2;
|
||||
|
||||
[DoNotSerialize]
|
||||
[Inspectable, UnitHeaderInspectable("Branches")]
|
||||
public int branchCount
|
||||
{
|
||||
get => _branchCount;
|
||||
set => _branchCount = Mathf.Clamp(value, 2, 10);
|
||||
}
|
||||
|
||||
[DoNotSerialize]
|
||||
public Dictionary<ControlInput, ValueInput> branches { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when any selector is entered.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The selected value.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput selection { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
branches = new Dictionary<ControlInput, ValueInput>();
|
||||
|
||||
selection = ValueOutput<object>(nameof(selection));
|
||||
exit = ControlOutput(nameof(exit));
|
||||
|
||||
for (int i = 0; i < branchCount; i++)
|
||||
{
|
||||
var branchValue = ValueInput<object>("value_" + i);
|
||||
var branchControl = ControlInput("enter_" + i, (flow) => Select(flow, branchValue));
|
||||
|
||||
Requirement(branchValue, branchControl);
|
||||
Assignment(branchControl, selection);
|
||||
Succession(branchControl, exit);
|
||||
|
||||
branches.Add(branchControl, branchValue);
|
||||
}
|
||||
}
|
||||
|
||||
public ControlOutput Select(Flow flow, ValueInput branchValue)
|
||||
{
|
||||
flow.SetValue(selection, flow.GetValue(branchValue));
|
||||
|
||||
return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Selects a value from a set by switching over an integer.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Select On Integer")]
|
||||
[UnitShortTitle("Select")]
|
||||
[UnitSubtitle("On Integer")]
|
||||
[UnitOrder(8)]
|
||||
public class SelectOnInteger : SelectUnit<int>
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Selects a value from a set by switching over a string.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Select On String")]
|
||||
[UnitShortTitle("Select")]
|
||||
[UnitSubtitle("On String")]
|
||||
[UnitOrder(7)]
|
||||
public class SelectOnString : SelectUnit<string>
|
||||
{
|
||||
[Serialize]
|
||||
[Inspectable, UnitHeaderInspectable("Ignore Case")]
|
||||
[InspectorToggleLeft]
|
||||
public bool ignoreCase { get; set; }
|
||||
|
||||
protected override bool Matches(string a, string b)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return string.Equals(a, b, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Selects a value from a set by checking if a condition is true or false.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Select")]
|
||||
[TypeIcon(typeof(ISelectUnit))]
|
||||
[UnitOrder(6)]
|
||||
public sealed class SelectUnit : Unit, ISelectUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// The condition to check.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput condition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value to return if the condition is true.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("True")]
|
||||
public ValueInput ifTrue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value to return if the condition is false.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("False")]
|
||||
public ValueInput ifFalse { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The returned value.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput selection { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
condition = ValueInput<bool>(nameof(condition));
|
||||
ifTrue = ValueInput<object>(nameof(ifTrue)).AllowsNull();
|
||||
ifFalse = ValueInput<object>(nameof(ifFalse)).AllowsNull();
|
||||
selection = ValueOutput(nameof(selection), Branch).Predictable();
|
||||
|
||||
Requirement(condition, selection);
|
||||
Requirement(ifTrue, selection);
|
||||
Requirement(ifFalse, selection);
|
||||
}
|
||||
|
||||
public object Branch(Flow flow)
|
||||
{
|
||||
return flow.GetValue(flow.GetValue<bool>(condition) ? ifTrue : ifFalse);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[TypeIcon(typeof(ISelectUnit))]
|
||||
public abstract class SelectUnit<T> : Unit, ISelectUnit
|
||||
{
|
||||
// Using L<KVP> instead of Dictionary to allow null key
|
||||
[DoNotSerialize]
|
||||
public List<KeyValuePair<T, ValueInput>> branches { get; private set; }
|
||||
|
||||
[Inspectable, Serialize]
|
||||
public List<T> options { get; set; } = new List<T>();
|
||||
|
||||
/// <summary>
|
||||
/// The value on which to select.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput selector { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The output value to return if the selector doesn't match any other option.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput @default { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The selected value.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput selection { get; private set; }
|
||||
|
||||
public override bool canDefine => options != null;
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
selection = ValueOutput(nameof(selection), Result).Predictable();
|
||||
|
||||
selector = ValueInput<T>(nameof(selector));
|
||||
|
||||
Requirement(selector, selection);
|
||||
|
||||
branches = new List<KeyValuePair<T, ValueInput>>();
|
||||
|
||||
foreach (var option in options)
|
||||
{
|
||||
var key = "%" + option;
|
||||
|
||||
if (!valueInputs.Contains(key))
|
||||
{
|
||||
var branch = ValueInput<object>(key).AllowsNull();
|
||||
branches.Add(new KeyValuePair<T, ValueInput>(option, branch));
|
||||
Requirement(branch, selection);
|
||||
}
|
||||
}
|
||||
|
||||
@default = ValueInput<object>(nameof(@default));
|
||||
|
||||
Requirement(@default, selection);
|
||||
}
|
||||
|
||||
protected virtual bool Matches(T a, T b)
|
||||
{
|
||||
return Equals(a, b);
|
||||
}
|
||||
|
||||
public object Result(Flow flow)
|
||||
{
|
||||
var selector = flow.GetValue<T>(this.selector);
|
||||
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
if (Matches(branch.Key, selector))
|
||||
{
|
||||
return flow.GetValue(branch.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return flow.GetValue(@default);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the output ports in order.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(13)]
|
||||
public sealed class Sequence : Unit
|
||||
{
|
||||
[SerializeAs(nameof(outputCount))]
|
||||
private int _outputCount = 2;
|
||||
|
||||
/// <summary>
|
||||
/// The entry point for the sequence.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
[Inspectable, InspectorLabel("Steps"), UnitHeaderInspectable("Steps")]
|
||||
public int outputCount
|
||||
{
|
||||
get => _outputCount;
|
||||
set => _outputCount = Mathf.Clamp(value, 1, 10);
|
||||
}
|
||||
|
||||
[DoNotSerialize]
|
||||
public ReadOnlyCollection<ControlOutput> multiOutputs { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInputCoroutine(nameof(enter), Enter, EnterCoroutine);
|
||||
|
||||
var _multiOutputs = new List<ControlOutput>();
|
||||
|
||||
multiOutputs = _multiOutputs.AsReadOnly();
|
||||
|
||||
for (var i = 0; i < outputCount; i++)
|
||||
{
|
||||
var output = ControlOutput(i.ToString());
|
||||
|
||||
Succession(enter, output);
|
||||
|
||||
_multiOutputs.Add(output);
|
||||
}
|
||||
}
|
||||
|
||||
private ControlOutput Enter(Flow flow)
|
||||
{
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
foreach (var output in multiOutputs)
|
||||
{
|
||||
flow.Invoke(output);
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IEnumerator EnterCoroutine(Flow flow)
|
||||
{
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
foreach (var output in multiOutputs)
|
||||
{
|
||||
yield return output;
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
}
|
||||
|
||||
public void CopyFrom(Sequence source)
|
||||
{
|
||||
base.CopyFrom(source);
|
||||
outputCount = source.outputCount;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Branches flow by switching over an enum.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Switch On Enum")]
|
||||
[UnitShortTitle("Switch")]
|
||||
[UnitSubtitle("On Enum")]
|
||||
[UnitOrder(3)]
|
||||
[TypeIcon(typeof(IBranchUnit))]
|
||||
public sealed class SwitchOnEnum : Unit, IBranchUnit
|
||||
{
|
||||
[DoNotSerialize]
|
||||
public Dictionary<Enum, ControlOutput> branches { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
[Inspectable, UnitHeaderInspectable]
|
||||
[TypeFilter(Enums = true, Classes = false, Interfaces = false, Structs = false, Primitives = false)]
|
||||
public Type enumType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The entry point for the switch.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The enum value on which to switch.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput @enum { get; private set; }
|
||||
|
||||
public override bool canDefine => enumType != null && enumType.IsEnum;
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
branches = new Dictionary<Enum, ControlOutput>();
|
||||
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
|
||||
@enum = ValueInput(enumType, nameof(@enum));
|
||||
|
||||
Requirement(@enum, enter);
|
||||
|
||||
foreach (var valueByName in EnumUtility.ValuesByNames(enumType))
|
||||
{
|
||||
var enumName = valueByName.Key;
|
||||
var enumValue = valueByName.Value;
|
||||
|
||||
// Just like in C#, duplicate switch labels for the same underlying value is prohibited
|
||||
if (!branches.ContainsKey(enumValue))
|
||||
{
|
||||
var branch = ControlOutput("%" + enumName);
|
||||
|
||||
branches.Add(enumValue, branch);
|
||||
|
||||
Succession(enter, branch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ControlOutput Enter(Flow flow)
|
||||
{
|
||||
var @enum = (Enum)flow.GetValue(this.@enum, enumType);
|
||||
|
||||
if (branches.ContainsKey(@enum))
|
||||
{
|
||||
return branches[@enum];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Branches flow by switching over an integer.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Switch On Integer")]
|
||||
[UnitShortTitle("Switch")]
|
||||
[UnitSubtitle("On Integer")]
|
||||
[UnitOrder(5)]
|
||||
public class SwitchOnInteger : SwitchUnit<int>
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Branches flow by switching over a string.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitTitle("Switch On String")]
|
||||
[UnitShortTitle("Switch")]
|
||||
[UnitSubtitle("On String")]
|
||||
[UnitOrder(4)]
|
||||
public class SwitchOnString : SwitchUnit<string>
|
||||
{
|
||||
[Serialize]
|
||||
[Inspectable, UnitHeaderInspectable("Ignore Case")]
|
||||
[InspectorToggleLeft]
|
||||
public bool ignoreCase { get; set; }
|
||||
|
||||
protected override bool Matches(string a, string b)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return string.Equals(a, b, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[TypeIcon(typeof(IBranchUnit))]
|
||||
public abstract class SwitchUnit<T> : Unit, IBranchUnit
|
||||
{
|
||||
// Using L<KVP> instead of Dictionary to allow null key
|
||||
[DoNotSerialize]
|
||||
public List<KeyValuePair<T, ControlOutput>> branches { get; private set; }
|
||||
|
||||
[Inspectable, Serialize]
|
||||
public List<T> options { get; set; } = new List<T>();
|
||||
|
||||
/// <summary>
|
||||
/// The entry point for the switch.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value on which to switch.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput selector { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The branch to take if the input value does not match any other option.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput @default { get; private set; }
|
||||
|
||||
public override bool canDefine => options != null;
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
|
||||
selector = ValueInput<T>(nameof(selector));
|
||||
|
||||
Requirement(selector, enter);
|
||||
|
||||
branches = new List<KeyValuePair<T, ControlOutput>>();
|
||||
|
||||
foreach (var option in options)
|
||||
{
|
||||
var key = "%" + option;
|
||||
|
||||
if (!controlOutputs.Contains(key))
|
||||
{
|
||||
var branch = ControlOutput(key);
|
||||
branches.Add(new KeyValuePair<T, ControlOutput>(option, branch));
|
||||
Succession(enter, branch);
|
||||
}
|
||||
}
|
||||
|
||||
@default = ControlOutput(nameof(@default));
|
||||
Succession(enter, @default);
|
||||
}
|
||||
|
||||
protected virtual bool Matches(T a, T b)
|
||||
{
|
||||
return Equals(a, b);
|
||||
}
|
||||
|
||||
public ControlOutput Enter(Flow flow)
|
||||
{
|
||||
var selector = flow.GetValue<T>(this.selector);
|
||||
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
if (Matches(branch.Key, selector))
|
||||
{
|
||||
return branch.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return @default;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Throws an exception.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(16)]
|
||||
public sealed class Throw : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether a custom exception object should be specified manually.
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
[Inspectable, UnitHeaderInspectable("Custom")]
|
||||
[InspectorToggleLeft]
|
||||
public bool custom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The entry point to throw the exception.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The message of the exception.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput message { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The exception to throw.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput exception { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
if (custom)
|
||||
{
|
||||
enter = ControlInput(nameof(enter), ThrowCustom);
|
||||
exception = ValueInput<Exception>(nameof(exception));
|
||||
Requirement(exception, enter);
|
||||
}
|
||||
else
|
||||
{
|
||||
enter = ControlInput(nameof(enter), ThrowMessage);
|
||||
message = ValueInput(nameof(message), string.Empty);
|
||||
Requirement(message, enter);
|
||||
}
|
||||
}
|
||||
|
||||
private ControlOutput ThrowCustom(Flow flow)
|
||||
{
|
||||
throw flow.GetValue<Exception>(exception);
|
||||
}
|
||||
|
||||
private ControlOutput ThrowMessage(Flow flow)
|
||||
{
|
||||
throw new Exception(flow.GetValue<string>(message));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Toggles the control flow with on and off triggers.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(18)]
|
||||
[UnitFooterPorts(ControlInputs = true, ControlOutputs = true)]
|
||||
public sealed class ToggleFlow : Unit, IGraphElementWithData
|
||||
{
|
||||
public class Data : IGraphElementData
|
||||
{
|
||||
public bool isOn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the toggle should start in the on state.
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
[Inspectable]
|
||||
[UnitHeaderInspectable("Start On")]
|
||||
[InspectorToggleLeft]
|
||||
public bool startOn { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point to the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to turn on the flow through the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("On")]
|
||||
public ControlInput turnOn { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to turn off the flow through the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Off")]
|
||||
public ControlInput turnOff { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to toggle the flow through the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlInput toggle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggered on entry if the flow is on.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("On")]
|
||||
public ControlOutput exitOn { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggered on entry if the flow is off.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Off")]
|
||||
public ControlOutput exitOff { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the flow gets turned on.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput turnedOn { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the flow gets turned off.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput turnedOff { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the flow is currently on.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueOutput isOn { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
turnOn = ControlInput(nameof(turnOn), TurnOn);
|
||||
turnOff = ControlInput(nameof(turnOff), TurnOff);
|
||||
toggle = ControlInput(nameof(toggle), Toggle);
|
||||
|
||||
exitOn = ControlOutput(nameof(exitOn));
|
||||
exitOff = ControlOutput(nameof(exitOff));
|
||||
turnedOn = ControlOutput(nameof(turnedOn));
|
||||
turnedOff = ControlOutput(nameof(turnedOff));
|
||||
|
||||
isOn = ValueOutput(nameof(isOn), IsOn);
|
||||
|
||||
Succession(enter, exitOn);
|
||||
Succession(enter, exitOff);
|
||||
Succession(turnOn, turnedOn);
|
||||
Succession(turnOff, turnedOff);
|
||||
Succession(toggle, turnedOn);
|
||||
Succession(toggle, turnedOff);
|
||||
}
|
||||
|
||||
public IGraphElementData CreateData()
|
||||
{
|
||||
return new Data() { isOn = startOn };
|
||||
}
|
||||
|
||||
private bool IsOn(Flow flow)
|
||||
{
|
||||
return flow.stack.GetElementData<Data>(this).isOn;
|
||||
}
|
||||
|
||||
private ControlOutput Enter(Flow flow)
|
||||
{
|
||||
return IsOn(flow) ? exitOn : exitOff;
|
||||
}
|
||||
|
||||
private ControlOutput TurnOn(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
if (data.isOn)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
data.isOn = true;
|
||||
|
||||
return turnedOn;
|
||||
}
|
||||
|
||||
private ControlOutput TurnOff(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
if (!data.isOn)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
data.isOn = false;
|
||||
|
||||
return turnedOff;
|
||||
}
|
||||
|
||||
private ControlOutput Toggle(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
data.isOn = !data.isOn;
|
||||
|
||||
return data.isOn ? turnedOn : turnedOff;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Toggles between two values with on and off triggers.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(19)]
|
||||
[UnitFooterPorts(ControlInputs = true, ControlOutputs = true)]
|
||||
public sealed class ToggleValue : Unit, IGraphElementWithData
|
||||
{
|
||||
public class Data : IGraphElementData
|
||||
{
|
||||
public bool isOn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the toggle should start in the on state.
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
[Inspectable]
|
||||
[UnitHeaderInspectable("Start On")]
|
||||
[InspectorToggleLeft]
|
||||
public bool startOn { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to turn on the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("On")]
|
||||
public ControlInput turnOn { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to turn off the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Off")]
|
||||
public ControlInput turnOff { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to toggle the state of the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlInput toggle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value to return if the toggle is on.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput onValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value to return if the toggle is off.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput offValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the flow gets turned on.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput turnedOn { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the flow gets turned off.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput turnedOff { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the flow is currently on.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueOutput isOn { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the toggle selected depending on the state.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
turnOn = ControlInput(nameof(turnOn), TurnOn);
|
||||
turnOff = ControlInput(nameof(turnOff), TurnOff);
|
||||
toggle = ControlInput(nameof(toggle), Toggle);
|
||||
|
||||
onValue = ValueInput<object>(nameof(onValue));
|
||||
offValue = ValueInput<object>(nameof(offValue));
|
||||
|
||||
turnedOn = ControlOutput(nameof(turnedOn));
|
||||
turnedOff = ControlOutput(nameof(turnedOff));
|
||||
|
||||
isOn = ValueOutput(nameof(isOn), IsOn);
|
||||
value = ValueOutput(nameof(value), Value);
|
||||
|
||||
Requirement(onValue, value);
|
||||
Requirement(offValue, value);
|
||||
Succession(turnOn, turnedOn);
|
||||
Succession(turnOff, turnedOff);
|
||||
Succession(toggle, turnedOn);
|
||||
Succession(toggle, turnedOff);
|
||||
}
|
||||
|
||||
public IGraphElementData CreateData()
|
||||
{
|
||||
return new Data() { isOn = startOn };
|
||||
}
|
||||
|
||||
private bool IsOn(Flow flow)
|
||||
{
|
||||
return flow.stack.GetElementData<Data>(this).isOn;
|
||||
}
|
||||
|
||||
private ControlOutput TurnOn(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
if (data.isOn)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
data.isOn = true;
|
||||
|
||||
return turnedOn;
|
||||
}
|
||||
|
||||
private ControlOutput TurnOff(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
if (!data.isOn)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
data.isOn = false;
|
||||
|
||||
return turnedOff;
|
||||
}
|
||||
|
||||
private ControlOutput Toggle(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
data.isOn = !data.isOn;
|
||||
|
||||
return data.isOn ? turnedOn : turnedOff;
|
||||
}
|
||||
|
||||
private object Value(Flow flow)
|
||||
{
|
||||
var data = flow.stack.GetElementData<Data>(this);
|
||||
|
||||
return flow.GetValue(data.isOn ? onValue : offValue);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles an exception if it occurs.
|
||||
/// </summary>
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(17)]
|
||||
[UnitFooterPorts(ControlOutputs = true)]
|
||||
public sealed class TryCatch : Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// The entry point for the try-catch block.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to attempt.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput @try { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute if an exception is thrown.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput @catch { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to execute afterwards, regardless of whether there was an exception.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ControlOutput @finally { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The exception that was thrown in the try block.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueOutput exception { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
[Inspectable, UnitHeaderInspectable]
|
||||
[TypeFilter(typeof(Exception), Matching = TypesMatching.AssignableToAll)]
|
||||
[TypeSet(TypeSet.SettingsAssembliesTypes)]
|
||||
public Type exceptionType { get; set; } = typeof(Exception);
|
||||
|
||||
public override bool canDefine => exceptionType != null && typeof(Exception).IsAssignableFrom(exceptionType);
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
@try = ControlOutput(nameof(@try));
|
||||
@catch = ControlOutput(nameof(@catch));
|
||||
@finally = ControlOutput(nameof(@finally));
|
||||
exception = ValueOutput(exceptionType, nameof(exception));
|
||||
|
||||
Assignment(enter, exception);
|
||||
Succession(enter, @try);
|
||||
Succession(enter, @catch);
|
||||
Succession(enter, @finally);
|
||||
}
|
||||
|
||||
public ControlOutput Enter(Flow flow)
|
||||
{
|
||||
if (flow.isCoroutine)
|
||||
{
|
||||
throw new NotSupportedException("Coroutines cannot catch exceptions.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
flow.Invoke(@try);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (exceptionType.IsInstanceOfType(ex))
|
||||
{
|
||||
flow.SetValue(exception, ex);
|
||||
flow.Invoke(@catch);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
flow.Invoke(@finally);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Loops as long as a given condition is true.
|
||||
/// </summary>
|
||||
[UnitTitle("While Loop")]
|
||||
[UnitCategory("Control")]
|
||||
[UnitOrder(11)]
|
||||
public class While : LoopUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// The condition to check at each iteration to determine whether the loop should continue.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput condition { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
condition = ValueInput<bool>(nameof(condition));
|
||||
|
||||
Requirement(condition, enter);
|
||||
}
|
||||
|
||||
private int Start(Flow flow)
|
||||
{
|
||||
return flow.EnterLoop();
|
||||
}
|
||||
|
||||
private bool CanMoveNext(Flow flow)
|
||||
{
|
||||
return flow.GetValue<bool>(condition);
|
||||
}
|
||||
|
||||
protected override ControlOutput Loop(Flow flow)
|
||||
{
|
||||
var loop = Start(flow);
|
||||
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
while (flow.LoopIsNotBroken(loop) && CanMoveNext(flow))
|
||||
{
|
||||
flow.Invoke(body);
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
|
||||
flow.ExitLoop(loop);
|
||||
|
||||
return exit;
|
||||
}
|
||||
|
||||
protected override IEnumerator LoopCoroutine(Flow flow)
|
||||
{
|
||||
var loop = Start(flow);
|
||||
|
||||
var stack = flow.PreserveStack();
|
||||
|
||||
while (flow.LoopIsNotBroken(loop) && CanMoveNext(flow))
|
||||
{
|
||||
yield return body;
|
||||
|
||||
flow.RestoreStack(stack);
|
||||
}
|
||||
|
||||
flow.DisposePreservedStack(stack);
|
||||
|
||||
flow.ExitLoop(loop);
|
||||
|
||||
yield return exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when an animation event points to TriggerAnimationEvent.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Animation")]
|
||||
[UnitShortTitle("Animation Event")]
|
||||
[UnitTitle("Animation Event")]
|
||||
[TypeIcon(typeof(Animation))]
|
||||
[DisplayName("Visual Scripting Animation Event")]
|
||||
public sealed class BoltAnimationEvent : MachineEventUnit<AnimationEvent>
|
||||
{
|
||||
protected override string hookName => EventHooks.AnimationEvent;
|
||||
|
||||
/// <summary>
|
||||
/// The string parameter passed to the event.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("String")]
|
||||
public ValueOutput stringParameter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The float parameter passed to the event.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Float")]
|
||||
public ValueOutput floatParameter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The integer parameter passed to the function.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Integer")]
|
||||
public ValueOutput intParameter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Unity object parameter passed to the function.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Object")]
|
||||
public ValueOutput objectReferenceParameter { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
stringParameter = ValueOutput<string>(nameof(stringParameter));
|
||||
floatParameter = ValueOutput<float>(nameof(floatParameter));
|
||||
intParameter = ValueOutput<int>(nameof(intParameter));
|
||||
objectReferenceParameter = ValueOutput<UnityObject>(nameof(objectReferenceParameter));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, AnimationEvent args)
|
||||
{
|
||||
flow.SetValue(stringParameter, args.stringParameter);
|
||||
flow.SetValue(floatParameter, args.floatParameter);
|
||||
flow.SetValue(intParameter, args.intParameter);
|
||||
flow.SetValue(objectReferenceParameter, args.objectReferenceParameter);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when an animation event points to TriggerAnimationEvent.
|
||||
/// This version allows you to use the string parameter as the event name.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Animation")]
|
||||
[UnitShortTitle("Animation Event")]
|
||||
[UnitTitle("Named Animation Event")]
|
||||
[TypeIcon(typeof(Animation))]
|
||||
[DisplayName("Visual Scripting Named Animation Event")]
|
||||
public sealed class BoltNamedAnimationEvent : MachineEventUnit<AnimationEvent>
|
||||
{
|
||||
protected override string hookName => EventHooks.AnimationEvent;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the event. The event will only trigger if this value
|
||||
/// is equal to the string parameter passed in the animation event.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The float parameter passed to the event.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Float")]
|
||||
public ValueOutput floatParameter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The integer parameter passed to the function.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Integer")]
|
||||
public ValueOutput intParameter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Unity object parameter passed to the function.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Object")]
|
||||
public ValueOutput objectReferenceParameter { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
name = ValueInput(nameof(name), string.Empty);
|
||||
|
||||
floatParameter = ValueOutput<float>(nameof(floatParameter));
|
||||
intParameter = ValueOutput<int>(nameof(intParameter));
|
||||
objectReferenceParameter = ValueOutput<GameObject>(nameof(objectReferenceParameter));
|
||||
}
|
||||
|
||||
protected override bool ShouldTrigger(Flow flow, AnimationEvent animationEvent)
|
||||
{
|
||||
return CompareNames(flow, name, animationEvent.stringParameter);
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, AnimationEvent animationEvent)
|
||||
{
|
||||
flow.SetValue(floatParameter, animationEvent.floatParameter);
|
||||
flow.SetValue(intParameter, animationEvent.intParameter);
|
||||
flow.SetValue(objectReferenceParameter, animationEvent.objectReferenceParameter);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called by the Animator Component immediately before it updates its internal IK system.
|
||||
/// This callback can be used to set the positions of the IK goals and their respective weights.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Animation")]
|
||||
public sealed class OnAnimatorIK : GameObjectEventUnit<int>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(AnimatorMessageListener);
|
||||
protected override string hookName => EventHooks.OnAnimatorIK;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the layer on which the IK solver is called.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueOutput layerIndex { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
layerIndex = ValueOutput<int>(nameof(layerIndex));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, int layerIndex)
|
||||
{
|
||||
flow.SetValue(this.layerIndex, layerIndex);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called at each frame after the state machines and the animations have been evaluated, but before On Animator IK.
|
||||
/// This callback can be used for processing animation movements for modifying root motion.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Animation")]
|
||||
public sealed class OnAnimatorMove : GameObjectEventUnit<EmptyEventArgs>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(AnimatorMessageListener);
|
||||
protected override string hookName => EventHooks.OnAnimatorMove;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the application gains focus.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Application")]
|
||||
public sealed class OnApplicationFocus : GlobalEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnApplicationFocus;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the application loses focus.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Application")]
|
||||
public sealed class OnApplicationLostFocus : GlobalEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnApplicationLostFocus;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the application pauses.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Application")]
|
||||
public sealed class OnApplicationPause : GlobalEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnApplicationPause;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the application quits.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Application")]
|
||||
public sealed class OnApplicationQuit : GlobalEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnApplicationQuit;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the application resumes.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Application")]
|
||||
public sealed class OnApplicationResume : GlobalEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnApplicationResume;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when a UnityEvent points to TriggerUnityEvent.
|
||||
/// </summary>
|
||||
[UnitCategory("Events")]
|
||||
[UnitTitle("UnityEvent")]
|
||||
[UnitOrder(2)]
|
||||
[DisplayName("Visual Scripting Unity Event")]
|
||||
public sealed class BoltUnityEvent : MachineEventUnit<string>
|
||||
{
|
||||
protected override string hookName => EventHooks.UnityEvent;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the event. The event will only trigger if this value
|
||||
/// is equal to the string parameter passed in the UnityEvent.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput name { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
name = ValueInput(nameof(name), string.Empty);
|
||||
}
|
||||
|
||||
protected override bool ShouldTrigger(Flow flow, string name)
|
||||
{
|
||||
return CompareNames(flow, this.name, name);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// A special named event with any amount of parameters called manually with the 'Trigger Custom Event' unit.
|
||||
/// </summary>
|
||||
[UnitCategory("Events")]
|
||||
[UnitOrder(0)]
|
||||
public sealed class CustomEvent : GameObjectEventUnit<CustomEventArgs>
|
||||
{
|
||||
public override Type MessageListenerType => null;
|
||||
protected override string hookName => EventHooks.Custom;
|
||||
|
||||
[SerializeAs(nameof(argumentCount))]
|
||||
private int _argumentCount;
|
||||
|
||||
[DoNotSerialize]
|
||||
[Inspectable, UnitHeaderInspectable("Arguments")]
|
||||
public int argumentCount
|
||||
{
|
||||
get => _argumentCount;
|
||||
set => _argumentCount = Mathf.Clamp(value, 0, 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the event.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueInput name { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public List<ValueOutput> argumentPorts { get; } = new List<ValueOutput>();
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
name = ValueInput(nameof(name), string.Empty);
|
||||
|
||||
argumentPorts.Clear();
|
||||
|
||||
for (var i = 0; i < argumentCount; i++)
|
||||
{
|
||||
argumentPorts.Add(ValueOutput<object>("argument_" + i));
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShouldTrigger(Flow flow, CustomEventArgs args)
|
||||
{
|
||||
return CompareNames(flow, name, args.name);
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, CustomEventArgs args)
|
||||
{
|
||||
for (var i = 0; i < argumentCount; i++)
|
||||
{
|
||||
flow.SetValue(argumentPorts[i], args.arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Trigger(GameObject target, string name, params object[] args)
|
||||
{
|
||||
EventBus.Trigger(EventHooks.Custom, target, new CustomEventArgs(name, args));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public struct CustomEventArgs
|
||||
{
|
||||
public readonly string name;
|
||||
|
||||
public readonly object[] arguments;
|
||||
|
||||
public CustomEventArgs(string name, params object[] arguments)
|
||||
{
|
||||
this.name = name;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Use to draw gizmos that are always drawn in the editor.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Editor")]
|
||||
public sealed class OnDrawGizmos : ManualEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnDrawGizmos;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Use to draw gizmos that are drawn in the editor when the object is selected.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Editor")]
|
||||
public sealed class OnDrawGizmosSelected : ManualEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnDrawGizmosSelected;
|
||||
}
|
||||
}
|
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[SerializationVersion("A")]
|
||||
[SpecialUnit]
|
||||
public abstract class EventUnit<TArgs> : Unit, IEventUnit, IGraphElementWithData, IGraphEventHandler<TArgs>
|
||||
{
|
||||
public class Data : IGraphElementData
|
||||
{
|
||||
public EventHook hook;
|
||||
|
||||
public Delegate handler;
|
||||
|
||||
public bool isListening;
|
||||
|
||||
public HashSet<Flow> activeCoroutines = new HashSet<Flow>();
|
||||
}
|
||||
|
||||
public virtual IGraphElementData CreateData()
|
||||
{
|
||||
return new Data();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run this event in a coroutine, enabling asynchronous flow like wait units.
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
[Inspectable]
|
||||
[InspectorExpandTooltip]
|
||||
public bool coroutine { get; set; } = false;
|
||||
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput trigger { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
protected abstract bool register { get; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
isControlRoot = true;
|
||||
|
||||
trigger = ControlOutput(nameof(trigger));
|
||||
}
|
||||
|
||||
public virtual EventHook GetHook(GraphReference reference)
|
||||
{
|
||||
throw new InvalidImplementationException($"Missing event hook for '{this}'.");
|
||||
}
|
||||
|
||||
public virtual void StartListening(GraphStack stack)
|
||||
{
|
||||
var data = stack.GetElementData<Data>(this);
|
||||
|
||||
if (data.isListening)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (register)
|
||||
{
|
||||
var reference = stack.ToReference();
|
||||
var hook = GetHook(reference);
|
||||
Action<TArgs> handler = args => Trigger(reference, args);
|
||||
EventBus.Register(hook, handler);
|
||||
|
||||
data.hook = hook;
|
||||
data.handler = handler;
|
||||
}
|
||||
|
||||
data.isListening = true;
|
||||
}
|
||||
|
||||
public virtual void StopListening(GraphStack stack)
|
||||
{
|
||||
var data = stack.GetElementData<Data>(this);
|
||||
|
||||
if (!data.isListening)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The coroutine's flow will dispose at the next frame, letting us
|
||||
// keep the current flow for clean up operations if needed
|
||||
foreach (var activeCoroutine in data.activeCoroutines)
|
||||
{
|
||||
activeCoroutine.StopCoroutine(false);
|
||||
}
|
||||
|
||||
if (register)
|
||||
{
|
||||
EventBus.Unregister(data.hook, data.handler);
|
||||
data.handler = null;
|
||||
}
|
||||
|
||||
data.isListening = false;
|
||||
}
|
||||
|
||||
public override void Uninstantiate(GraphReference instance)
|
||||
{
|
||||
// Here, we're relying on the fact that OnDestroy calls Uninstantiate.
|
||||
// We need to force-dispose any remaining coroutine to avoid
|
||||
// memory leaks, because OnDestroy on the runner will not keep
|
||||
// executing MoveNext() until our soft-destroy call at the end of Flow.Coroutine
|
||||
// or even dispose the coroutine's enumerator (!).
|
||||
var data = instance.GetElementData<Data>(this);
|
||||
var coroutines = data.activeCoroutines.ToHashSetPooled();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
new FrameDelayedCallback(() => StopAllCoroutines(coroutines), 1);
|
||||
#else
|
||||
StopAllCoroutines(coroutines);
|
||||
#endif
|
||||
|
||||
base.Uninstantiate(instance);
|
||||
}
|
||||
|
||||
static void StopAllCoroutines(HashSet<Flow> activeCoroutines)
|
||||
{
|
||||
// The coroutine's flow will dispose instantly, thus modifying
|
||||
// the activeCoroutines registry while we enumerate over it
|
||||
foreach (var activeCoroutine in activeCoroutines)
|
||||
{
|
||||
activeCoroutine.StopCoroutineImmediate();
|
||||
}
|
||||
activeCoroutines.Free();
|
||||
}
|
||||
|
||||
public bool IsListening(GraphPointer pointer)
|
||||
{
|
||||
if (!pointer.hasData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return pointer.GetElementData<Data>(this).isListening;
|
||||
}
|
||||
|
||||
public void Trigger(GraphReference reference, TArgs args)
|
||||
{
|
||||
var flow = Flow.New(reference);
|
||||
|
||||
if (!ShouldTrigger(flow, args))
|
||||
{
|
||||
flow.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
AssignArguments(flow, args);
|
||||
|
||||
Run(flow);
|
||||
}
|
||||
|
||||
protected virtual bool ShouldTrigger(Flow flow, TArgs args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void AssignArguments(Flow flow, TArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
private void Run(Flow flow)
|
||||
{
|
||||
if (flow.enableDebug)
|
||||
{
|
||||
var editorData = flow.stack.GetElementDebugData<IUnitDebugData>(this);
|
||||
|
||||
editorData.lastInvokeFrame = EditorTimeBinding.frame;
|
||||
editorData.lastInvokeTime = EditorTimeBinding.time;
|
||||
}
|
||||
|
||||
if (coroutine)
|
||||
{
|
||||
flow.StartCoroutine(trigger, flow.stack.GetElementData<Data>(this).activeCoroutines);
|
||||
}
|
||||
else
|
||||
{
|
||||
flow.Run(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
protected static bool CompareNames(Flow flow, ValueInput namePort, string calledName)
|
||||
{
|
||||
Ensure.That(nameof(calledName)).IsNotNull(calledName);
|
||||
|
||||
return calledName.Trim().Equals(flow.GetValue<string>(namePort)?.Trim(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class GenericGuiEventUnit : GameObjectEventUnit<BaseEventData>
|
||||
{
|
||||
/// <summary>
|
||||
/// The event data.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput data { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
data = ValueOutput<BaseEventData>(nameof(data));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, BaseEventData data)
|
||||
{
|
||||
flow.SetValue(this.data, data);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called on the drag object when dragging is about to begin.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(OnDrag))]
|
||||
[UnitOrder(16)]
|
||||
public sealed class OnBeginDrag : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnBeginDragMessageListener);
|
||||
protected override string hookName => EventHooks.OnBeginDrag;
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when a user clicks the button and releases it.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(Button))]
|
||||
[UnitOrder(1)]
|
||||
public sealed class OnButtonClick : GameObjectEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnButtonClick;
|
||||
public override Type MessageListenerType => typeof(UnityOnButtonClickMessageListener);
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the cancel button is pressed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(25)]
|
||||
public sealed class OnCancel : GenericGuiEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnCancelMessageListener);
|
||||
protected override string hookName => EventHooks.OnCancel;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the pointer deselects the GUI element.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(23)]
|
||||
public sealed class OnDeselect : GenericGuiEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnDeselectMessageListener);
|
||||
protected override string hookName => EventHooks.OnDeselect;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// When draging is occuring this will be called every time the cursor is moved.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(17)]
|
||||
public sealed class OnDrag : PointerEventUnit
|
||||
{
|
||||
protected override string hookName => EventHooks.OnDrag;
|
||||
public override Type MessageListenerType => typeof(UnityOnDragMessageListener);
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called on a target that can accept a drop.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(OnDrag))]
|
||||
[UnitOrder(19)]
|
||||
public sealed class OnDrop : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnDropMessageListener);
|
||||
protected override string hookName => EventHooks.OnDrop;
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the current value of the dropdown has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(Dropdown))]
|
||||
[UnitOrder(4)]
|
||||
public sealed class OnDropdownValueChanged : GameObjectEventUnit<int>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnDropdownValueChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnDropdownValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the newly selected option.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueOutput index { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The text of the newly selected option.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueOutput text { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
index = ValueOutput<int>(nameof(index));
|
||||
text = ValueOutput<string>(nameof(text));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, int index)
|
||||
{
|
||||
flow.SetValue(this.index, index);
|
||||
flow.SetValue(text, flow.GetValue<Dropdown>(target).options[index].text);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called on the drag object when a drag finishes.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(OnDrag))]
|
||||
[UnitOrder(18)]
|
||||
public sealed class OnEndDrag : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnEndDragMessageListener);
|
||||
protected override string hookName => EventHooks.OnEndDrag;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Use to draw immediate mode GUI components.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(GUI))]
|
||||
[UnitOrder(0)]
|
||||
public sealed class OnGUI : GlobalEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.OnGUI;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the user finishes editing the text content either by submitting or by clicking somewhere that removes the
|
||||
/// focus from the input field.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(InputField))]
|
||||
[UnitOrder(3)]
|
||||
public sealed class OnInputFieldEndEdit : GameObjectEventUnit<string>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnInputFieldEndEditMessageListener);
|
||||
protected override string hookName => EventHooks.OnInputFieldEndEdit;
|
||||
|
||||
/// <summary>
|
||||
/// The new text content of the input field.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
value = ValueOutput<string>(nameof(value));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, string value)
|
||||
{
|
||||
flow.SetValue(this.value, value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the text content of the input field has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(InputField))]
|
||||
[UnitOrder(2)]
|
||||
public sealed class OnInputFieldValueChanged : GameObjectEventUnit<string>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnInputFieldValueChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnInputFieldValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The new text content of the input field.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
value = ValueOutput<string>(nameof(value));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, string value)
|
||||
{
|
||||
flow.SetValue(this.value, value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when a move event occurs.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(21)]
|
||||
public sealed class OnMove : GameObjectEventUnit<AxisEventData>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnMoveMessageListener);
|
||||
protected override string hookName => EventHooks.OnMove;
|
||||
|
||||
/// <summary>
|
||||
/// The axis event data.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput data { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
data = ValueOutput<AxisEventData>(nameof(data));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, AxisEventData data)
|
||||
{
|
||||
flow.SetValue(this.data, data);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the pointer clicks the GUI element.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(11)]
|
||||
public sealed class OnPointerClick : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnPointerClickMessageListener);
|
||||
protected override string hookName => EventHooks.OnPointerClick;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the pointer presses the GUI element.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(12)]
|
||||
public sealed class OnPointerDown : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnPointerDownMessageListener);
|
||||
protected override string hookName => EventHooks.OnPointerDown;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the pointer enters the GUI element.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(14)]
|
||||
public sealed class OnPointerEnter : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnPointerEnterMessageListener);
|
||||
protected override string hookName => EventHooks.OnPointerEnter;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the pointer exits the GUI element.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(15)]
|
||||
public sealed class OnPointerExit : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnPointerExitMessageListener);
|
||||
protected override string hookName => EventHooks.OnPointerExit;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the pointer releases the GUI element.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(13)]
|
||||
public sealed class OnPointerUp : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnPointerUpMessageListener);
|
||||
protected override string hookName => EventHooks.OnPointerUp;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when a mouse wheel scrolls.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(20)]
|
||||
public sealed class OnScroll : PointerEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnScrollMessageListener);
|
||||
protected override string hookName => EventHooks.OnScroll;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the current value of the scrollbar has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(ScrollRect))]
|
||||
[UnitOrder(7)]
|
||||
public sealed class OnScrollRectValueChanged : GameObjectEventUnit<Vector2>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnScrollRectValueChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnScrollRectValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The new scroll position of the scroll rect.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
value = ValueOutput<Vector2>(nameof(value));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, Vector2 value)
|
||||
{
|
||||
flow.SetValue(this.value, value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the current value of the scrollbar has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(Scrollbar))]
|
||||
[UnitOrder(6)]
|
||||
public sealed class OnScrollbarValueChanged : GameObjectEventUnit<float>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnScrollbarValueChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnScrollbarValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The new position value of the scrollbar.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
value = ValueOutput<float>(nameof(value));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, float value)
|
||||
{
|
||||
flow.SetValue(this.value, value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the pointer selects the GUI element.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(22)]
|
||||
public sealed class OnSelect : GenericGuiEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnSelectMessageListener);
|
||||
protected override string hookName => EventHooks.OnSelect;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the current value of the slider has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(Slider))]
|
||||
[UnitOrder(8)]
|
||||
public sealed class OnSliderValueChanged : GameObjectEventUnit<float>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnSliderValueChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnSliderValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The new numeric value of the slider.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
value = ValueOutput<float>(nameof(value));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, float value)
|
||||
{
|
||||
flow.SetValue(this.value, value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the submit button is pressed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[UnitOrder(24)]
|
||||
public sealed class OnSubmit : GenericGuiEventUnit
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnSubmitMessageListener);
|
||||
protected override string hookName => EventHooks.OnSubmit;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the current value of the toggle has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/GUI")]
|
||||
[TypeIcon(typeof(Toggle))]
|
||||
[UnitOrder(5)]
|
||||
public sealed class OnToggleValueChanged : GameObjectEventUnit<bool>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnToggleValueChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnToggleValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The new boolean value of the toggle.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput value { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
value = ValueOutput<bool>(nameof(value));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, bool value)
|
||||
{
|
||||
flow.SetValue(this.value, value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class PointerEventUnit : GameObjectEventUnit<PointerEventData>
|
||||
{
|
||||
/// <summary>
|
||||
/// The pointer event data.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput data { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
data = ValueOutput<PointerEventData>(nameof(data));
|
||||
}
|
||||
|
||||
protected override void AssignArguments(Flow flow, PointerEventData data)
|
||||
{
|
||||
flow.SetValue(this.data, data);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class GameObjectEventUnit<TArgs> : EventUnit<TArgs>, IGameObjectEventUnit
|
||||
{
|
||||
protected sealed override bool register => true;
|
||||
|
||||
public abstract Type MessageListenerType { get; }
|
||||
|
||||
public new class Data : EventUnit<TArgs>.Data
|
||||
{
|
||||
public GameObject target;
|
||||
}
|
||||
|
||||
public override IGraphElementData CreateData()
|
||||
{
|
||||
return new Data();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game object that listens for the event.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[NullMeansSelf]
|
||||
[PortLabel("Target")]
|
||||
[PortLabelHidden]
|
||||
public ValueInput target { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
target = ValueInput<GameObject>(nameof(target), null).NullMeansSelf();
|
||||
}
|
||||
|
||||
public override EventHook GetHook(GraphReference reference)
|
||||
{
|
||||
if (!reference.hasData)
|
||||
{
|
||||
return hookName;
|
||||
}
|
||||
|
||||
var data = reference.GetElementData<Data>(this);
|
||||
|
||||
return new EventHook(hookName, data.target);
|
||||
}
|
||||
|
||||
protected virtual string hookName => throw new InvalidImplementationException($"Missing event hook for '{this}'.");
|
||||
|
||||
private void UpdateTarget(GraphStack stack)
|
||||
{
|
||||
var data = stack.GetElementData<Data>(this);
|
||||
|
||||
var wasListening = data.isListening;
|
||||
|
||||
var newTarget = Flow.FetchValue<GameObject>(target, stack.ToReference());
|
||||
|
||||
if (newTarget != data.target)
|
||||
{
|
||||
if (wasListening)
|
||||
{
|
||||
StopListening(stack);
|
||||
}
|
||||
|
||||
data.target = newTarget;
|
||||
|
||||
if (wasListening)
|
||||
{
|
||||
StartListening(stack, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void StartListening(GraphStack stack, bool updateTarget)
|
||||
{
|
||||
if (updateTarget)
|
||||
{
|
||||
UpdateTarget(stack);
|
||||
}
|
||||
|
||||
var data = stack.GetElementData<Data>(this);
|
||||
|
||||
if (data.target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UnityThread.allowsAPI)
|
||||
{
|
||||
if (MessageListenerType != null) // can be null. CustomEvent doesn't need a message listener
|
||||
MessageListener.AddTo(MessageListenerType, data.target);
|
||||
}
|
||||
|
||||
base.StartListening(stack);
|
||||
}
|
||||
|
||||
public override void StartListening(GraphStack stack)
|
||||
{
|
||||
StartListening(stack, true);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class GlobalEventUnit<TArgs> : EventUnit<TArgs>
|
||||
{
|
||||
protected override bool register => true;
|
||||
|
||||
protected virtual string hookName => throw new InvalidImplementationException();
|
||||
|
||||
public override EventHook GetHook(GraphReference reference)
|
||||
{
|
||||
return hookName;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the list of children of the transform of the game object has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Hierarchy")]
|
||||
public sealed class OnTransformChildrenChanged : GameObjectEventUnit<EmptyEventArgs>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnTransformChildrenChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnTransformChildrenChanged;
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the parent property of the transform of the game object has changed.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Hierarchy")]
|
||||
public sealed class OnTransformParentChanged : GameObjectEventUnit<EmptyEventArgs>
|
||||
{
|
||||
public override Type MessageListenerType => typeof(UnityOnTransformParentChangedMessageListener);
|
||||
protected override string hookName => EventHooks.OnTransformParentChanged;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IEventUnit : IUnit, IGraphEventListener
|
||||
{
|
||||
bool coroutine { get; }
|
||||
}
|
||||
public interface IGameObjectEventUnit : IEventUnit
|
||||
{
|
||||
Type MessageListenerType { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
#if PACKAGE_INPUT_SYSTEM_EXISTS
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Unity.VisualScripting.InputSystem
|
||||
{
|
||||
public enum InputActionChangeOption
|
||||
{
|
||||
OnPressed,
|
||||
OnHold,
|
||||
OnReleased,
|
||||
}
|
||||
|
||||
public enum OutputType
|
||||
{
|
||||
Button,
|
||||
Float,
|
||||
Vector2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A configurable event to handle input system events.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Input")]
|
||||
public abstract class OnInputSystemEvent : MachineEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName =>
|
||||
UnityEngine.InputSystem.InputSystem.settings.updateMode ==
|
||||
InputSettings.UpdateMode.ProcessEventsInDynamicUpdate
|
||||
? EventHooks.Update
|
||||
: EventHooks.FixedUpdate;
|
||||
|
||||
protected abstract OutputType OutputType { get; }
|
||||
|
||||
[Serialize, Inspectable, UnitHeaderInspectable]
|
||||
public InputActionChangeOption InputActionChangeType;
|
||||
|
||||
/// <summary>
|
||||
/// The InputAction. If it's selected from the dropdown populated from the target PlayerInput, the widget will
|
||||
/// serialize a fake PlayerAction with the right ID/name, but it will need to be compared to the actual action
|
||||
/// fetched at runtime from the playerInput's action asset as the fake one won't trigger any event
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput InputAction { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The target PlayerInput component. if null, the InputAction is expected to be fetched dynamically
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
[NullMeansSelf]
|
||||
public ValueInput Target { get; private set; }
|
||||
|
||||
[PortLabelHidden]
|
||||
public ValueOutput FloatValue { get; private set; }
|
||||
[PortLabelHidden]
|
||||
public ValueOutput Vector2Value { get; private set; }
|
||||
|
||||
private InputAction m_Action;
|
||||
|
||||
private bool m_WasRunning;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the last value, and returned by the output port. This intermediary value is there to enable
|
||||
/// "fetching" from the value port, which happens when the value is read, but the node has never been executed.
|
||||
/// Typical use case is "on some other event, read the value of the axis/joystick even if it was never used"
|
||||
/// </summary>
|
||||
private Vector2 m_Value;
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
Target = ValueInput(typeof(PlayerInput), nameof(Target));
|
||||
Target.SetDefaultValue(null);
|
||||
Target.NullMeansSelf();
|
||||
|
||||
InputAction = ValueInput(typeof(InputAction), nameof(InputAction));
|
||||
InputAction.SetDefaultValue(default(InputAction));
|
||||
|
||||
switch (OutputType)
|
||||
{
|
||||
case OutputType.Button:
|
||||
break;
|
||||
case OutputType.Float:
|
||||
// the getValue delegate is what enables fetching
|
||||
FloatValue = ValueOutput<float>(nameof(FloatValue), _ => m_Value.x);
|
||||
break;
|
||||
case OutputType.Vector2:
|
||||
// the getValue delegate is what enables fetching
|
||||
Vector2Value = ValueOutput<Vector2>(nameof(Vector2Value), _ => m_Value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public override void StartListening(GraphStack stack)
|
||||
{
|
||||
base.StartListening(stack);
|
||||
var graphReference = stack.ToReference();
|
||||
|
||||
var pi = Flow.FetchValue<PlayerInput>(Target, graphReference);
|
||||
var inputAction = Flow.FetchValue<InputAction>(InputAction, graphReference);
|
||||
if (inputAction == null)
|
||||
return;
|
||||
m_Action = pi
|
||||
? pi.actions.FindAction(inputAction.id)
|
||||
: inputAction.actionMap != null ? inputAction : null;
|
||||
}
|
||||
|
||||
public override void StopListening(GraphStack stack)
|
||||
{
|
||||
base.StopListening(stack);
|
||||
m_Action = null;
|
||||
}
|
||||
|
||||
protected override bool ShouldTrigger(Flow flow, EmptyEventArgs args)
|
||||
{
|
||||
if (m_Action == null)
|
||||
return false;
|
||||
|
||||
bool shouldtrigger;
|
||||
// "Started" is true while the button is held, triggered is true only one frame. hence what looks like a bug but isn't
|
||||
switch (InputActionChangeType)
|
||||
{
|
||||
case InputActionChangeOption.OnPressed:
|
||||
shouldtrigger = m_Action.triggered; // started is true too long
|
||||
break;
|
||||
case InputActionChangeOption.OnHold:
|
||||
shouldtrigger = m_Action.phase == InputActionPhase.Started; // triggered is only true one frame
|
||||
break;
|
||||
case InputActionChangeOption.OnReleased:
|
||||
shouldtrigger = m_WasRunning && m_Action.phase != InputActionPhase.Started; // never equal to InputActionPhase.Cancelled when polling
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
DoAssignArguments(flow);
|
||||
|
||||
// Hack - can't make sense of the action phase when polled (always Started or Waiting). Fallback on "== Started"
|
||||
m_WasRunning = m_Action.phase == InputActionPhase.Started;
|
||||
|
||||
return shouldtrigger;
|
||||
}
|
||||
|
||||
private void DoAssignArguments(Flow flow)
|
||||
{
|
||||
switch (OutputType)
|
||||
{
|
||||
case OutputType.Button:
|
||||
break;
|
||||
case OutputType.Float:
|
||||
var readValue = m_Action.ReadValue<float>();
|
||||
m_Value.Set(readValue, 0);
|
||||
flow.SetValue(FloatValue, readValue);
|
||||
break;
|
||||
case OutputType.Vector2:
|
||||
var vector2 = m_Action.ReadValue<Vector2>();
|
||||
m_Value = vector2;
|
||||
flow.SetValue(Vector2Value, vector2);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OnInputSystemEventButton : OnInputSystemEvent
|
||||
{
|
||||
protected override OutputType OutputType => OutputType.Button;
|
||||
}
|
||||
|
||||
public class OnInputSystemEventFloat : OnInputSystemEvent
|
||||
{
|
||||
protected override OutputType OutputType => OutputType.Float;
|
||||
}
|
||||
|
||||
public class OnInputSystemEventVector2 : OnInputSystemEvent
|
||||
{
|
||||
protected override OutputType OutputType => OutputType.Vector2;
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// A configurable event to handle global button input.
|
||||
/// </summary>
|
||||
[UnitCategory("Events/Input")]
|
||||
public sealed class OnButtonInput : MachineEventUnit<EmptyEventArgs>
|
||||
{
|
||||
protected override string hookName => EventHooks.Update;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the button that received input.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabel("Name")]
|
||||
public ValueInput buttonName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of input.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
public ValueInput action { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
base.Definition();
|
||||
|
||||
buttonName = ValueInput(nameof(buttonName), string.Empty);
|
||||
action = ValueInput(nameof(action), PressState.Down);
|
||||
}
|
||||
|
||||
protected override bool ShouldTrigger(Flow flow, EmptyEventArgs args)
|
||||
{
|
||||
var buttonName = flow.GetValue<string>(this.buttonName);
|
||||
var action = flow.GetValue<PressState>(this.action);
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case PressState.Down: return Input.GetButtonDown(buttonName);
|
||||
case PressState.Up: return Input.GetButtonUp(buttonName);
|
||||
case PressState.Hold: return Input.GetButton(buttonName);
|
||||
default: throw new UnexpectedEnumValueException<PressState>(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user