using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; using UnityEngine; namespace Misaki.GraphView { [Serializable] public abstract class ExecutableNode : DataNode, ISlotContainer, IExecutable { [SerializeReference] private List _inputs = new(); [SerializeReference] private List _outputs = new(); private bool _isExecuted; public ReadOnlyCollection Inputs => _inputs.AsReadOnly(); public ReadOnlyCollection Outputs => _outputs.AsReadOnly(); public bool IsExecuted => _isExecuted; public Action OnExecutionStarted; public Action OnExecutionCompleted; public Action OnExecutionFailed; public Action OnExecuteFlagCleared; public override void Initialize(GraphObject graph) { base.Initialize(graph); InitializeSlot(); } public virtual void InitializeSlot() { var type = GetType(); var fields = type.GetFields(ConstResource.NODE_FIELD_BINDING_FLAGS); var inputSlotIndex = 0; var outputSlotIndex = 0; foreach (var field in fields) { var inputAttribute = field.GetCustomAttribute(); if (inputAttribute != null) { var inputSlot = new Slot(this, new SlotData { slotName = field.Name, nodeID = Id, slotIndex = inputSlotIndex++, direction = SlotDirection.Input, valueType = field.FieldType.FullName }); _inputs.Add(inputSlot); continue; } var outputAttribute = field.GetCustomAttribute(); if (outputAttribute != null) { var outputSlot = new Slot(this, new SlotData { slotName = field.Name, nodeID = Id, slotIndex = outputSlotIndex++, direction = SlotDirection.Output, valueType = field.FieldType.FullName }); _outputs.Add(outputSlot); } } } /// public ISlot GetSlot(int index, SlotDirection direction) { return direction switch { SlotDirection.Input => Inputs[index], SlotDirection.Output => Outputs[index], _ => null }; } /// public void UnlinkAllSlots() { foreach (var input in Inputs) { input.UnlinkAll(); graphObject.RemoveAllConnectionsForSlot(input); } foreach (var output in Outputs) { output.UnlinkAll(); graphObject.RemoveAllConnectionsForSlot(output); } } /// public void Execute() { if (_isExecuted) { return; } OnExecutionStarted?.Invoke(); Inputs.PullData(OnPullData); if (!graphObject.GraphProcessor.IsRunning) { return; } if (!OnExecute()) { graphObject.GraphProcessor.Break(); OnExecutionFailed?.Invoke(); return; } Outputs.PushData(OnPushData); _isExecuted = true; OnExecutionCompleted?.Invoke(); } /// public void ClearExecutionFlag() { _isExecuted = false; OnExecuteFlagCleared?.Invoke(); } protected virtual void OnPullData(ISlot input) { } protected virtual void OnPushData(ISlot output) { } /// /// The execution logic of the node. /// /// Return true if the execution is success, otherwise false protected abstract bool OnExecute(); } }