NodeScript+ 导入了个 UI Extend

Signed-off-by: TRADER_FOER <lhf190@outlook.com>
This commit is contained in:
2026-05-23 21:05:16 +08:00
parent 7ea1f1d8c3
commit 51878f15ae
531 changed files with 198095 additions and 144473 deletions

View File

@@ -0,0 +1,280 @@
using System;
using System.Collections.Generic;
using Ichni.RhythmGame;
using UnityEngine;
namespace Ichni.NodeScript
{
// ==================== 图入口 ====================
public class NodeStart : NodeBase
{
public Output<Signal> exec = new("Exec");
public Output<GameElement> element = new("Element");
[NonSerialized] public GameElement boundElement;
public override LoopResult Loop()
{
exec.SetValue(Signal.Default);
element.SetValue(boundElement);
return LoopResult.Complete();
}
}
public class NodeEntry : NodeBase
{
public Output<Signal> exec = new("Exec");
public override LoopResult Loop()
{
exec.SetValue(Signal.Default);
return LoopResult.Complete();
}
}
// ==================== 统一多类型数学运算 ====================
public class NodeMath : NodeBase
{
enum Op { Add, Subtract, Multiply, Divide }
Op _op;
public InputAny a = new("A");
public InputAny b = new("B") { IsFixedType = true };
public OutputAny result = new("Result");
static readonly Dictionary<(Type, Op), Func<object, object, object>> _ops = new()
{
[(typeof(float), Op.Add)] = (x, y) => (float)x + (float)y,
[(typeof(float), Op.Subtract)] = (x, y) => (float)x - (float)y,
[(typeof(float), Op.Multiply)] = (x, y) => (float)x * (float)y,
[(typeof(float), Op.Divide)] = (x, y) => (float)y != 0f ? (float)x / (float)y : 0f,
[(typeof(int), Op.Add)] = (x, y) => (int)x + (int)y,
[(typeof(int), Op.Subtract)] = (x, y) => (int)x - (int)y,
[(typeof(int), Op.Multiply)] = (x, y) => (int)x * (int)y,
[(typeof(int), Op.Divide)] = (x, y) => (int)y != 0 ? (int)x / (int)y : 0,
[(typeof(Vector2), Op.Add)] = (x, y) => (Vector2)x + (Vector2)y,
[(typeof(Vector2), Op.Subtract)] = (x, y) => (Vector2)x - (Vector2)y,
[(typeof(Vector2), Op.Multiply)] = (x, y) => (Vector2)x * (float)y,
[(typeof(Vector2), Op.Divide)] = (x, y) => (float)y != 0f ? (Vector2)x / (float)y : Vector2.zero,
[(typeof(Vector3), Op.Add)] = (x, y) => (Vector3)x + (Vector3)y,
[(typeof(Vector3), Op.Subtract)] = (x, y) => (Vector3)x - (Vector3)y,
[(typeof(Vector3), Op.Multiply)] = (x, y) => (Vector3)x * (float)y,
[(typeof(Vector3), Op.Divide)] = (x, y) => (float)y != 0f ? (Vector3)x / (float)y : Vector3.zero,
[(typeof(Color), Op.Add)] = (x, y) => (Color)x + (Color)y,
[(typeof(Color), Op.Subtract)] = (x, y) => (Color)x - (Color)y,
[(typeof(Color), Op.Multiply)] = (x, y) => (Color)x * (float)y,
[(typeof(string), Op.Add)] = (x, y) => (string)x + (string)y,
};
public override void BuildUI(NodeUIBuilder ui)
{
ui.Dropdown("Op", new[] { "+", "-", "*", "/" }, 0, i => _op = (Op)i);
}
public override LoopResult Loop()
{
if (!a.IsConnected && !b.IsConnected) return LoopResult.Complete();
if (!EnsureInputsReady(out var hang)) return hang;
var type = a.DataType ?? b.DataType ?? typeof(float);
// 单输入 → 直通
if (!a.IsConnected) { result.SetValueRaw(SafeGet(b)); return LoopResult.Complete(); }
if (!b.IsConnected) { result.SetValueRaw(SafeGet(a)); return LoopResult.Complete(); }
// 双输入 → LUT 运算
var key = (type, _op);
if (_ops.TryGetValue(key, out var func))
result.SetValueRaw(func(SafeGet(a), SafeGet(b)));
else
result.SetValueRaw(SafeGet(a)); // fallback: 直通 a
return LoopResult.Complete();
}
static object SafeGet(InputAny input)
{
try { return input.GetValue(); }
catch { return null; }
}
}
// ==================== 分支 ====================
public class NodeBranch : NodeBase
{
public Input<Signal> exec = new("Exec");
public InputAny condition = new("Condition");
public Output<Signal> trueOut = new("True");
public Output<Signal> falseOut = new("False");
public override LoopResult Loop()
{
if (!exec.IsConnected) return LoopResult.Complete();
if (!EnsureInputsReady(out var hang)) return hang;
float cond = condition.IsConnected ? condition.GetValue<float>() : 0f;
// int → float 兼容
if (condition.DataType == typeof(int))
cond = condition.GetValue<int>();
if (cond > 0f) trueOut.SetValue(Signal.Default);
else falseOut.SetValue(Signal.Default);
return LoopResult.Complete();
}
}
// ==================== For 循环多周期状态机Rect 容器留待 Phase 4 ====================
public class NodeForLoop : NodeBase
{
public Input<Signal> exec = new("Exec");
public InputAny count = new("Count");
public Output<Signal> loopBody = new("LoopBody");
public Output<int> index = new("Index");
public Output<Signal> completed = new("Completed");
int _current;
int _total;
bool _started;
bool _waiting;
public override LoopResult Loop()
{
if (!exec.IsConnected) return LoopResult.Complete();
if (!_started && !EnsureInputsReady(out var hang)) return hang;
if (!_started)
{
_total = count.IsConnected ? count.GetValue<int>() : 0;
_current = 0;
_started = true;
}
// 空转一周期让下游消费上一轮输出
if (_waiting) { _waiting = false; return LoopResult.Wait(); }
if (_current < _total)
{
index.SetValue(_current);
loopBody.SetValue(Signal.Default);
_current++;
_waiting = true;
return LoopResult.Repeat();
}
_started = false;
completed.SetValue(Signal.Default);
return LoopResult.Complete();
}
}
// ==================== GameElement 操作 ====================
public class NodeGameElement : NodeBase
{
public Input<Signal> exec = new("Exec");
public Input<GameElement> RootNode = new("Root");
public Input<GameElement> SourceNode = new("Source");
public Output<GameElement> newElement = new("OutPut");
public Output<Signal> completed = new("Done");
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
var root = RootNode.Value;
var source = SourceNode.Value;
if (source == null)
{
ElementFolder.GenerateElement("Folder(Cp)", Guid.NewGuid(), new List<string>(), true, RootNode.IsConnected ? root : null);
}
else if (root != null)
{
EditorManager.instance.operationManager.CopyPasteDeleteModule.CopyElement(source);
EditorManager.instance.operationManager.CopyPasteDeleteModule.PasteElement(root);
}
completed.SetValue(Signal.Default);
return LoopResult.Complete();
}
}
public class NodeGetTransform : NodeBase
{
public Input<GameElement> element = new("Element");
public Output<Vector3> position = new("Pos"), rotation = new("Rot"), scale = new("Scl");
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
if (element.Value is IHaveTransformSubmodule ts)
{
position.SetValue(ts.transformSubmodule.originalPosition);
rotation.SetValue(ts.transformSubmodule.originalEulerAngles);
scale.SetValue(ts.transformSubmodule.originalScale);
}
return LoopResult.Complete();
}
}
public class NodeSetTransform : NodeBase
{
public Input<Signal> exec = new("Exec");
public Input<GameElement> element = new("Element");
public Input<Vector3> Pos = new("Pos"), Rot = new("Rot"), Scale = new("Scl");
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
if (element.Value is IHaveTransformSubmodule ts)
{
if (Pos.IsConnected) { ts.transformSubmodule.originalPosition = Pos.Value; ts.transformSubmodule.positionDirtyMark = true; }
if (Rot.IsConnected) { ts.transformSubmodule.originalEulerAngles = Rot.Value; ts.transformSubmodule.eulerAnglesDirtyMark = true; }
if (Scale.IsConnected) { ts.transformSubmodule.originalScale = Scale.Value; ts.transformSubmodule.scaleDirtyMark = true; }
}
return LoopResult.Complete();
}
}
public class NodeChildByIndex : NodeBase
{
public Input<GameElement> parent = new("Parent");
public int index;
public Output<GameElement> child = new("Child");
public override void BuildUI(NodeUIBuilder ui) { ui.FloatField("Idx", 0, i => index = (int)i); }
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
var p = parent.Value;
if (p != null && index >= 0 && index < p.childElementList.Count)
child.SetValue(p.childElementList[index]);
return LoopResult.Complete();
}
}
public class NodeChildCount : NodeBase
{
public Input<GameElement> parent = new("Parent");
public Output<int> count = new("Count");
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
var p = parent.Value;
count.SetValue(p != null ? p.childElementList.Count : 0);
return LoopResult.Complete();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: da71a524cc8687941840c85db8590b1c

View File

@@ -0,0 +1,445 @@
using System;
using System.Collections.Generic;
using Ichni.RhythmGame;
using UnityEngine;
namespace Ichni.NodeScript
{
// ==================== 通用常量 ====================
public class NodeConst : NodeBase
{
enum ValueType { Float, Int, Bool, Vector2, Vector3, Color }
ValueType _type;
public float floatVal;
public int intVal;
public bool boolVal;
public Vector2 vector2Val;
public Vector3 vector3Val;
public Color colorVal;
public OutputAny value = new("Value");
public override void InitConnectors()
{
base.InitConnectors();
value.LockType(typeof(float));
}
public override void BuildUI(NodeUIBuilder ui)
{
ui.TypeDropdown(new[] { "float", "int", "bool", "Vector2", "Vector3", "Color" }, 0, i =>
{
_type = (ValueType)i;
Type t = _type switch
{
ValueType.Float => typeof(float),
ValueType.Int => typeof(int),
ValueType.Bool => typeof(bool),
ValueType.Vector2 => typeof(Vector2),
ValueType.Vector3 => typeof(Vector3),
ValueType.Color => typeof(Color),
_ => typeof(float),
};
value.LockType(t);
});
switch (_type)
{
case ValueType.Float: ui.FloatField("F", 0f, v => floatVal = v); break;
case ValueType.Int: ui.FloatField("I", 0f, v => intVal = (int)v); break;
case ValueType.Bool: ui.Toggle("B", false, v => boolVal = v); break;
case ValueType.Vector2: ui.FloatField("X", 0f, v => vector2Val.x = v); ui.FloatField("Y", 0f, v => vector2Val.y = v); break;
case ValueType.Vector3: ui.FloatField("X", 0f, v => vector3Val.x = v); ui.FloatField("Y", 0f, v => vector3Val.y = v); ui.FloatField("Z", 0f, v => vector3Val.z = v); break;
case ValueType.Color: ui.FloatField("R", 1f, v => colorVal.r = v); ui.FloatField("G", 1f, v => colorVal.g = v); ui.FloatField("B", 1f, v => colorVal.b = v); ui.FloatField("A", 1f, v => colorVal.a = v); break;
}
}
public override LoopResult Loop()
{
object v = _type switch
{
ValueType.Float => floatVal,
ValueType.Int => intVal,
ValueType.Bool => boolVal,
ValueType.Vector2 => vector2Val,
ValueType.Vector3 => vector3Val,
ValueType.Color => colorVal,
_ => 0f,
};
value.SetValueRaw(v);
return LoopResult.Complete();
}
}
// ==================== 通用拆分 ====================
public class NodeSplit : NodeBase
{
public InputAny input = new("Input");
public Output<float> x = new("X"), y = new("Y"), z = new("Z"), w = new("W");
public override LoopResult Loop()
{
if (!input.IsConnected) return LoopResult.Complete();
if (!EnsureInputsReady(out var hang)) return hang;
var t = input.DataType;
if (t == typeof(Vector2))
{
var v = input.GetValue<Vector2>();
x.SetValue(v.x); y.SetValue(v.y);
}
else if (t == typeof(Vector3))
{
var v = input.GetValue<Vector3>();
x.SetValue(v.x); y.SetValue(v.y); z.SetValue(v.z);
}
else if (t == typeof(Color))
{
var v = input.GetValue<Color>();
x.SetValue(v.r); y.SetValue(v.g); z.SetValue(v.b); w.SetValue(v.a);
}
return LoopResult.Complete();
}
}
// ==================== 通用合并 ====================
public class NodeCombine : NodeBase
{
enum CombineType { Vector2, Vector3, Color }
CombineType _type;
public Input<float> x = new("X"), y = new("Y"), z = new("Z"), w = new("W");
public OutputAny output = new("Result");
public override void InitConnectors()
{
base.InitConnectors();
output.LockType(typeof(Vector2));
}
public override void BuildUI(NodeUIBuilder ui)
{
ui.TypeDropdown(new[] { "Vector2", "Vector3", "Color" }, 0, i =>
{
_type = (CombineType)i;
Type t = _type == CombineType.Vector2 ? typeof(Vector2) : _type == CombineType.Vector3 ? typeof(Vector3) : typeof(Color);
output.LockType(t);
});
}
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
object v = _type switch
{
CombineType.Vector2 => new Vector2(x.Value, y.Value),
CombineType.Vector3 => new Vector3(x.Value, y.Value, z.Value),
CombineType.Color => new Color(x.Value, y.Value, z.Value, w.Value),
_ => null,
};
output.SetValueRaw(v);
return LoopResult.Complete();
}
}
// ==================== Lerp 线性插值 ====================
public class NodeLerp : NodeBase
{
public InputAny a = new("A");
public InputAny b = new("B");
public InputAny t = new("T") { IsFixedType = true };
public OutputAny result = new("Result");
public override void InitConnectors()
{
base.InitConnectors();
t.LockType(typeof(float));
}
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
var type = a.DataType ?? b.DataType ?? typeof(float);
float factor = t.IsConnected ? Mathf.Clamp01(t.GetValue<float>()) : 0.5f;
object v = null;
if (type == typeof(float))
v = Mathf.Lerp(a.GetValue<float>(), b.GetValue<float>(), factor);
else if (type == typeof(int))
v = Mathf.RoundToInt(Mathf.Lerp(a.GetValue<int>(), b.GetValue<int>(), factor));
else if (type == typeof(Vector2))
v = Vector2.Lerp(a.GetValue<Vector2>(), b.GetValue<Vector2>(), factor);
else if (type == typeof(Vector3))
v = Vector3.Lerp(a.GetValue<Vector3>(), b.GetValue<Vector3>(), factor);
else if (type == typeof(Color))
v = Color.Lerp(a.GetValue<Color>(), b.GetValue<Color>(), factor);
result.SetValueRaw(v);
return LoopResult.Complete();
}
}
// ==================== 比较运算 ====================
public class NodeCompare : NodeBase
{
enum CmpOp { Equal, NotEqual, Greater, Less, GreaterOrEqual, LessOrEqual }
CmpOp _op;
public InputAny a = new("A");
public InputAny b = new("B");
public Output<bool> result = new("Result");
public override void BuildUI(NodeUIBuilder ui)
{
ui.Dropdown("Op", new[] { "==", "!=", ">", "<", ">=", "<=" }, 0, i => _op = (CmpOp)i);
}
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
var type = a.DataType ?? b.DataType ?? typeof(float);
bool r = false;
if (type == typeof(float)) r = Compare(a.GetValue<float>(), b.GetValue<float>());
else if (type == typeof(int)) r = Compare(a.GetValue<int>(), b.GetValue<int>());
else if (type == typeof(string)) r = Compare(a.GetValue<string>(), b.GetValue<string>());
result.SetValue(r);
return LoopResult.Complete();
}
bool Compare<T>(T x, T y) where T : IComparable<T>
{
int c = x.CompareTo(y);
return _op switch
{
CmpOp.Equal => c == 0,
CmpOp.NotEqual => c != 0,
CmpOp.Greater => c > 0,
CmpOp.Less => c < 0,
CmpOp.GreaterOrEqual => c >= 0,
CmpOp.LessOrEqual => c <= 0,
_ => false,
};
}
}
// ==================== 二选一 ====================
public class NodeSelect : NodeBase
{
public InputAny condition = new("Cond");
public InputAny trueValue = new("True");
public InputAny falseValue = new("False");
public OutputAny result = new("Result");
bool _manualCond;
public override void BuildUI(NodeUIBuilder ui)
{
ui.Toggle("Cond", false, v => _manualCond = v);
}
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
bool cond = condition.IsConnected ? condition.GetValue<bool>() : _manualCond;
// int → bool 兼容
if (!cond && condition.IsConnected && condition.DataType == typeof(int))
cond = condition.GetValue<int>() != 0;
var src = cond ? trueValue : falseValue;
result.SetValueRaw(src.GetValue());
return LoopResult.Complete();
}
}
// ==================== 万能赋值(反向写入) ====================
public class NodeSet : NodeBase
{
public InputAny targetRef = new("TargetRef");
public InputAny value = new("Value");
public override LoopResult Loop()
{
if (!targetRef.IsConnected) return LoopResult.Complete();
if (!EnsureInputsReady(out var hang)) return hang;
targetRef.WriteBack(value.GetValue());
return LoopResult.Complete();
}
}
// ==================== Variable<T> ====================
public class Variable<T> { public T Value; }
public class NodeVariable<T> : NodeBase
{
public Variable<T> var = new();
public Input<Signal> signal = new("Signal");
public Input<T> set = new("Set");
public Output<T> get = new("Value");
public override void InitConnectors()
{
base.InitConnectors();
(get as Output<T>).SetWriteBack(v => var.Value = v);
}
public override LoopResult Loop()
{
// Signal 有连接时必须等待(循环体内等待触发)
if (signal.IsConnected)
{
if (!EnsureInputsReady(out var hang)) return hang;
}
if (set.IsConnected)
var.Value = set.Value;
get.SetValue(var.Value);
return LoopResult.Complete();
}
}
// ==================== 调试 ====================
public class NodeDebugLog : NodeBase
{
public Input<Signal> exec = new("Exec");
public InputAny value = new("Value");
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
Debug.Log($"[DebugLog] {NodeName}: {value.GetValue()}");
return LoopResult.Complete();
}
}
public class NodeLog : NodeBase
{
public InputAny value = new("Value");
public string prefix = "";
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
Debug.Log($"[NodeLog] {prefix}{value.GetValue()}");
return LoopResult.Complete();
}
}
// ==================== List<T> ====================
public class NodeList<T> : NodeBase
{
public Variable<List<T>> list = new() { Value = new List<T>() };
public Output<List<T>> output = new("List");
public override LoopResult Loop()
{
output.SetValue(list.Value);
return LoopResult.Complete();
}
}
public class NodeListAdd<T> : NodeBase
{
public Input<List<T>> list = new("List");
public Input<T> item = new("Item");
public Output<List<T>> output = new("List");
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
var l = list.Value;
if (l != null && item.IsConnected) { l.Add(item.Value); output.SetValue(l); }
return LoopResult.Complete();
}
}
public class NodeListGet<T> : NodeBase
{
public Input<List<T>> list = new("List");
public int index;
public Output<T> element = new("Element");
public override void BuildUI(NodeUIBuilder ui) { ui.FloatField("Idx", 0, i => index = (int)i); }
public override LoopResult Loop()
{
if (!EnsureInputsReady(out var hang)) return hang;
var l = list.Value;
if (l != null && index >= 0 && index < l.Count) element.SetValue(l[index]);
return LoopResult.Complete();
}
}
public class NodeForEach : NodeBase
{
public Input<Signal> exec = new("Exec");
public InputAny list = new("List");
public Output<Signal> loopBody = new("LoopBody"), completed = new("Completed");
public OutputAny current = new("Current");
public OutputAny indexOut = new("Index");
System.Collections.IList _list;
int _i;
bool _started;
bool _waiting;
public override void InitConnectors()
{
base.InitConnectors();
indexOut.LockType(typeof(int));
}
public override void OnTypePropagated(Type t)
{
base.OnTypePropagated(t);
if (t != null && t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>))
current.LockType(t.GetGenericArguments()[0]);
}
public override LoopResult Loop()
{
if (!exec.IsConnected) return LoopResult.Complete();
if (!_started && !EnsureInputsReady(out var hang)) return hang;
if (!_started)
{
_list = list.GetValue() as System.Collections.IList;
if (_list == null || _list.Count == 0)
{
completed.SetValue(Signal.Default);
return LoopResult.Complete();
}
_i = 0;
_started = true;
}
if (_waiting) { _waiting = false; return LoopResult.Wait(); }
if (_i < _list.Count)
{
indexOut.SetValueRaw(_i);
current.SetValueRaw(_list[_i]);
loopBody.SetValue(Signal.Default);
_i++;
_waiting = true;
return LoopResult.Repeat();
}
_started = false;
completed.SetValue(Signal.Default);
return LoopResult.Complete();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d6d155dc132fc24448aa1896c024f9a6