導入
文字列フォーマットとかで動的に名前付き入力や出力が欲しくなる場面がある。
実装
まずは、 InputValue
に必要な値を持つクラスを作る。
[Serializable]
public class ArgsMetadata {
public string Name { get; set; }
public Type Type { get; set; }
}
定義した ArgsMetadata
クラスのリストから InputValue
や OutputValue
を生成する。
public class MyUnit : Unit {
[DoNotSerialize] List<ValueInput> CustomInputsSource { get; } = new List<ValueInput>();
[DoNotSerialize] List<ValueOutput> CustomsOutputsSource { get; } = new List<ValueOutput>();
[DoNotSerialize] public ReadOnlyCollection<ValueInput> CustomInputs { get; }
[DoNotSerialize] public ReadOnlyCollection<ValueOutput> CustomsOutputs { get; }
[Inspectable, InspectorWide, InspectorLabel("Custom Inputs")]
List<ArgsMetadata> CustomInputMetadata { get; }
[Inspectable, InspectorWide, InspectorLabel("Custom Outputs")]
List<ArgsMetadata> CustomOutputMetadata { get; }
public MyUnit() {
// ValueInput, ValueOuput を読み取り専用で公開する
CustomInputs = new ReadOnlyCollection<ValueInput>(CustomInputsSource);
CostomOutputs = new ReadOnlyCollection<ValueOutput>(CustomOutputsSource);
}
protected override void Definition() {
CustomInputsSource.Clear();
CustomsOutputsSource.Clear();
// メタデータを元にポートを作成する
if (CustomInputMetadata != null) {
foreach (var c in CustomInputMetadata) {
if (c.Type == null) continue;
CustomInputsSource.Add(ValueInput(c.Type, c.Name));
}
}
if (CustomOutputMetadata != null) {
foreach (var c in CustomOutputMetadata) {
if (c.Type == null) continue;
CustomsOutputsSource.Add(ValueOutput(c.Type, c.Name));
}
}
// フローや関連付けを適宜追加 //
}
}
このままでは GraphInspector に ArgsMetadata
のインスペクターが定義されていないと警告されてしまうため Inspector
クラスを継承して自前で GUI を実装する。 InspectorAttribute
属性を付与するとインスペクターに設定できる。
[Inspector(typeof(ArgsMetadata))]
public class ArgsMetadataInspector : Inspector {
private TypeFilter typeFilter;
public ArgsMetadataInspector(Metadata metadata) : base(metadata) {
typeFilter = metadata.Member("Type").GetAttribute<TypeFilter>() ?? TypeFilter.Any;
}
// GUIの高さを事前計算する
protected override float GetHeight(float width, GUIContent label) {
return EditorGUIUtility.singleLineHeight;
}
// GUIを描画する
protected override void OnGUI(Rect position, GUIContent label) {
var typeMetadata = metadata.Member("Type");
var nameMetadata = metadata.Member("Name");
// Type と Name で半々にする
var typePosition = new Rect(position.x, position.y,
position.width / 2, position.height);
var namePosition = new Rect(typePosition.x + typePosition.width, typePosition.y,
typePosition.width, typePosition.height);
// Type フィールド
BeginBlock(typeMetadata, position);
var newType = LudiqGUI.TypeField(typePosition, GUIContent.none,
(Type)typeMetadata.value, GetOptions);
if (EndBlock(typeMetadata)) {
typeMetadata.RecordUndo();
typeMetadata.value = newType;
}
// Name フィールド
BeginBlock(nameMetadata, namePosition);
var newNameValue = GUI.TextArea(namePosition, (string)nameMetadata.value);
if (EndBlock(nameMetadata)) {
nameMetadata.RecordUndo();
nameMetadata.value = newNameValue;
}
}
private IFuzzyOptionTree GetOptions() {
return new TypeOptionTree(Codebase.GetTypeSetFromAttribute(metadata), typeFilter);
}
}
LudiqGUI
という Visual Scripting 向けの GUI ヘルパークラスがあるので利用するとよい。