- 공통 보스 BT 프레임워크에 utility 패턴 역할과 준비/실행 브랜치를 추가 - 드로그 투척 패턴, 스킬, 투사체 이펙트를 연결하고 1인 플레이에서도 주 대상 fallback으로 발동되게 조정 - 투척 스폰 회전 계산을 보강해 zero vector 경고를 제거 - EnemyData와 VictoryUI 보스명을 투기장의 집행자 드로그 기준으로 정리 - Unity 플레이 검증으로 1인 호스트에서 투척 실행과 후속 전투 루프를 확인
323 lines
22 KiB
C#
323 lines
22 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace Colosseum.Editor
|
|
{
|
|
/// <summary>
|
|
/// 드로그 Behavior Graph authoring 자산을 현재 BT 우선순위 구조로 재생성합니다.
|
|
/// </summary>
|
|
public static class RebuildDrogBehaviorAuthoringGraph
|
|
{
|
|
private const string GraphAssetPath = "Assets/_Game/AI/BT_Drog.asset";
|
|
|
|
[MenuItem("Tools/Colosseum/Rebuild Drog Behavior Authoring Graph")]
|
|
private static void Rebuild()
|
|
{
|
|
UnityEngine.Object graphAsset = AssetDatabase.LoadMainAssetAtPath(GraphAssetPath);
|
|
if (graphAsset == null)
|
|
{
|
|
Debug.LogError($"[DrogBTRebuild] 그래프 자산을 찾을 수 없습니다: {GraphAssetPath}");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
Type authoringGraphType = graphAsset.GetType();
|
|
Assembly authoringAssembly = authoringGraphType.Assembly;
|
|
Assembly runtimeAssembly = typeof(Unity.Behavior.BehaviorGraph).Assembly;
|
|
|
|
MethodInfo createNodeMethod = authoringGraphType.BaseType?.GetMethod("CreateNode", BindingFlags.Instance | BindingFlags.Public);
|
|
MethodInfo connectEdgeMethod = authoringGraphType.BaseType?.GetMethod("ConnectEdge", BindingFlags.Instance | BindingFlags.Public);
|
|
MethodInfo buildRuntimeGraphMethod = authoringGraphType.GetMethod("BuildRuntimeGraph", BindingFlags.Instance | BindingFlags.Public);
|
|
MethodInfo saveAssetMethod = authoringGraphType.BaseType?.GetMethod("SaveAsset", BindingFlags.Instance | BindingFlags.Public);
|
|
MethodInfo setAssetDirtyMethod = authoringGraphType.BaseType?.GetMethod("SetAssetDirty", BindingFlags.Instance | BindingFlags.Public);
|
|
MethodInfo getNodeInfoMethod = authoringAssembly.GetType("Unity.Behavior.NodeRegistry", true)
|
|
?.GetMethod("GetInfo", BindingFlags.Static | BindingFlags.NonPublic);
|
|
|
|
if (createNodeMethod == null || connectEdgeMethod == null || buildRuntimeGraphMethod == null || saveAssetMethod == null || setAssetDirtyMethod == null || getNodeInfoMethod == null)
|
|
{
|
|
Debug.LogError("[DrogBTRebuild] Behavior Authoring 리플렉션 메서드를 찾지 못했습니다.");
|
|
return;
|
|
}
|
|
|
|
SerializedObject serializedObject = new SerializedObject(graphAsset);
|
|
SerializedProperty nodesProperty = serializedObject.FindProperty("m_Nodes");
|
|
if (nodesProperty == null)
|
|
{
|
|
Debug.LogError("[DrogBTRebuild] m_Nodes 프로퍼티를 찾지 못했습니다.");
|
|
return;
|
|
}
|
|
|
|
nodesProperty.ClearArray();
|
|
serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
|
|
|
object targetVariable = FindBlackboardVariableModel("Target");
|
|
if (targetVariable == null)
|
|
{
|
|
Debug.LogError("[DrogBTRebuild] Target 블랙보드 변수를 찾지 못했습니다.");
|
|
return;
|
|
}
|
|
|
|
object startNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.Start", true), new Vector2(420f, -620f));
|
|
object repeatNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.RepeaterModifier", true), new Vector2(420f, -470f));
|
|
object selectorNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SelectorComposite", true), new Vector2(420f, -280f));
|
|
|
|
object signatureSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-1020f, -40f));
|
|
object downSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-780f, -40f));
|
|
object utilitySequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-380f, -40f));
|
|
object leapSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(20f, -40f));
|
|
object slamSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(420f, -40f));
|
|
object mainSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(820f, -40f));
|
|
object slamFallbackSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(1220f, -40f));
|
|
object chaseSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(1620f, -40f));
|
|
|
|
object signatureRefreshNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(RefreshPrimaryTargetAction), new Vector2(-1140f, 240f));
|
|
object signatureHasTargetNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(ValidateTargetAction), new Vector2(-1020f, 240f));
|
|
object signatureReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckSignaturePatternReadyAction), new Vector2(-900f, 240f));
|
|
object signatureUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UseSignaturePatternAction), new Vector2(-780f, 240f));
|
|
|
|
object downSelectNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(SelectNearestDownedTargetAction), new Vector2(-740f, 240f));
|
|
object downReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckPunishPatternReadyAction), new Vector2(-620f, 240f));
|
|
object downUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UsePunishPatternAction), new Vector2(-500f, 240f));
|
|
|
|
object utilitySelectNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(SelectAlternateTargetByDistanceAction), new Vector2(-500f, 240f));
|
|
object utilityReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckUtilityPatternReadyAction), new Vector2(-380f, 240f));
|
|
object utilityUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UseUtilityPatternAction), new Vector2(-260f, 240f));
|
|
|
|
object leapSelectNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(SelectTargetByDistanceAction), new Vector2(-100f, 240f));
|
|
object leapReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckMobilityPatternReadyAction), new Vector2(20f, 240f));
|
|
object leapUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UseMobilityPatternAction), new Vector2(140f, 240f));
|
|
|
|
object slamRefreshNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(RefreshPrimaryTargetAction), new Vector2(240f, 240f));
|
|
object slamHasTargetNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(ValidateTargetAction), new Vector2(360f, 240f));
|
|
object slamRangeNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckTargetInAttackRangeAction), new Vector2(480f, 240f));
|
|
object slamTurnNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckSecondaryPatternTurnAction), new Vector2(600f, 240f));
|
|
object slamReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckSecondaryPatternReadyAction), new Vector2(720f, 240f));
|
|
object slamUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UseSecondaryPatternAction), new Vector2(840f, 240f));
|
|
|
|
object mainRefreshNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(RefreshPrimaryTargetAction), new Vector2(760f, 240f));
|
|
object mainHasTargetNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(ValidateTargetAction), new Vector2(880f, 240f));
|
|
object mainRangeNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckTargetInAttackRangeAction), new Vector2(1000f, 240f));
|
|
object mainReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckPrimaryPatternReadyAction), new Vector2(1120f, 240f));
|
|
object mainUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UsePrimaryPatternAction), new Vector2(1240f, 240f));
|
|
|
|
object fallbackRefreshNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(RefreshPrimaryTargetAction), new Vector2(1160f, 240f));
|
|
object fallbackHasTargetNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(ValidateTargetAction), new Vector2(1280f, 240f));
|
|
object fallbackRangeNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckTargetInAttackRangeAction), new Vector2(1400f, 240f));
|
|
object fallbackReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckSecondaryPatternReadyAction), new Vector2(1520f, 240f));
|
|
object fallbackUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UseSecondaryPatternAction), new Vector2(1640f, 240f));
|
|
|
|
object chaseRefreshNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(RefreshPrimaryTargetAction), new Vector2(1560f, 240f));
|
|
object chaseHasTargetNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(ValidateTargetAction), new Vector2(1680f, 240f));
|
|
object chaseUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(ChaseTargetAction), new Vector2(1800f, 240f));
|
|
|
|
Connect(graphAsset, connectEdgeMethod, GetDefaultOutputPort(startNode), GetDefaultInputPort(repeatNode));
|
|
Connect(graphAsset, connectEdgeMethod, GetDefaultOutputPort(repeatNode), GetDefaultInputPort(selectorNode));
|
|
ConnectChildren(graphAsset, connectEdgeMethod, selectorNode, signatureSequence, downSequence, utilitySequence, leapSequence, slamSequence, mainSequence, slamFallbackSequence, chaseSequence);
|
|
|
|
ConnectChildren(graphAsset, connectEdgeMethod, signatureSequence, signatureRefreshNode, signatureHasTargetNode, signatureReadyNode, signatureUseNode);
|
|
ConnectChildren(graphAsset, connectEdgeMethod, downSequence, downSelectNode, downReadyNode, downUseNode);
|
|
ConnectChildren(graphAsset, connectEdgeMethod, utilitySequence, utilitySelectNode, utilityReadyNode, utilityUseNode);
|
|
ConnectChildren(graphAsset, connectEdgeMethod, leapSequence, leapSelectNode, leapReadyNode, leapUseNode);
|
|
ConnectChildren(graphAsset, connectEdgeMethod, slamSequence, slamRefreshNode, slamHasTargetNode, slamRangeNode, slamTurnNode, slamReadyNode, slamUseNode);
|
|
ConnectChildren(graphAsset, connectEdgeMethod, mainSequence, mainRefreshNode, mainHasTargetNode, mainRangeNode, mainReadyNode, mainUseNode);
|
|
ConnectChildren(graphAsset, connectEdgeMethod, slamFallbackSequence, fallbackRefreshNode, fallbackHasTargetNode, fallbackRangeNode, fallbackReadyNode, fallbackUseNode);
|
|
ConnectChildren(graphAsset, connectEdgeMethod, chaseSequence, chaseRefreshNode, chaseHasTargetNode, chaseUseNode);
|
|
|
|
LinkTarget(signatureRefreshNode, targetVariable);
|
|
LinkTarget(signatureHasTargetNode, targetVariable);
|
|
LinkTarget(signatureUseNode, targetVariable);
|
|
LinkTarget(downSelectNode, targetVariable);
|
|
LinkTarget(downUseNode, targetVariable);
|
|
LinkTarget(utilitySelectNode, targetVariable);
|
|
LinkTarget(utilityUseNode, targetVariable);
|
|
LinkTarget(leapSelectNode, targetVariable);
|
|
LinkTarget(leapUseNode, targetVariable);
|
|
LinkTarget(slamRefreshNode, targetVariable);
|
|
LinkTarget(slamHasTargetNode, targetVariable);
|
|
LinkTarget(slamRangeNode, targetVariable);
|
|
LinkTarget(slamUseNode, targetVariable);
|
|
LinkTarget(mainRefreshNode, targetVariable);
|
|
LinkTarget(mainHasTargetNode, targetVariable);
|
|
LinkTarget(mainRangeNode, targetVariable);
|
|
LinkTarget(mainUseNode, targetVariable);
|
|
LinkTarget(fallbackRefreshNode, targetVariable);
|
|
LinkTarget(fallbackHasTargetNode, targetVariable);
|
|
LinkTarget(fallbackRangeNode, targetVariable);
|
|
LinkTarget(fallbackUseNode, targetVariable);
|
|
LinkTarget(chaseRefreshNode, targetVariable);
|
|
LinkTarget(chaseHasTargetNode, targetVariable);
|
|
LinkTarget(chaseUseNode, targetVariable);
|
|
|
|
SetStartRepeatFlags(startNode, repeat: true, allowMultipleRepeatsPerTick: false);
|
|
setAssetDirtyMethod.Invoke(graphAsset, new object[] { true });
|
|
buildRuntimeGraphMethod.Invoke(graphAsset, new object[] { true });
|
|
saveAssetMethod.Invoke(graphAsset, null);
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log("[DrogBTRebuild] 드로그 Behavior Graph authoring 자산 재구성이 완료되었습니다.");
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogException(exception);
|
|
}
|
|
}
|
|
|
|
private static object CreateNode(UnityEngine.Object graphAsset, MethodInfo createNodeMethod, MethodInfo getNodeInfoMethod, Type runtimeType, Vector2 position)
|
|
{
|
|
if (runtimeType == null)
|
|
throw new InvalidOperationException("[DrogBTRebuild] 런타임 타입이 null입니다.");
|
|
|
|
object nodeInfo = getNodeInfoMethod.Invoke(null, new object[] { runtimeType });
|
|
if (nodeInfo == null)
|
|
throw new InvalidOperationException($"[DrogBTRebuild] NodeInfo를 찾지 못했습니다: {runtimeType.FullName}");
|
|
|
|
Type nodeInfoType = nodeInfo.GetType();
|
|
FieldInfo modelTypeField = nodeInfoType.GetField("ModelType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
object serializableType = modelTypeField?.GetValue(nodeInfo);
|
|
PropertyInfo serializableTypeValueProperty = serializableType?.GetType().GetProperty("Type", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
Type modelType = serializableTypeValueProperty?.GetValue(serializableType) as Type;
|
|
if (modelType == null)
|
|
throw new InvalidOperationException($"[DrogBTRebuild] ModelType을 찾지 못했습니다: {runtimeType.FullName}");
|
|
|
|
return createNodeMethod.Invoke(graphAsset, new object[] { modelType, position, null, new object[] { nodeInfo } });
|
|
}
|
|
|
|
private static void ConnectChildren(UnityEngine.Object graphAsset, MethodInfo connectEdgeMethod, object parentNode, params object[] children)
|
|
{
|
|
object outputPort = GetDefaultOutputPort(parentNode);
|
|
for (int i = 0; i < children.Length; i++)
|
|
{
|
|
Connect(graphAsset, connectEdgeMethod, outputPort, GetDefaultInputPort(children[i]));
|
|
}
|
|
}
|
|
|
|
private static void Connect(UnityEngine.Object graphAsset, MethodInfo connectEdgeMethod, object outputPort, object inputPort)
|
|
{
|
|
if (outputPort == null || inputPort == null)
|
|
throw new InvalidOperationException("[DrogBTRebuild] 포트 연결 대상이 null입니다.");
|
|
|
|
connectEdgeMethod.Invoke(graphAsset, new[] { outputPort, inputPort });
|
|
}
|
|
|
|
private static object GetDefaultInputPort(object node)
|
|
{
|
|
return GetDefaultPort(node, "TryDefaultInputPortModel");
|
|
}
|
|
|
|
private static object GetDefaultOutputPort(object node)
|
|
{
|
|
return GetDefaultPort(node, "TryDefaultOutputPortModel");
|
|
}
|
|
|
|
private static object GetDefaultPort(object node, string methodName)
|
|
{
|
|
MethodInfo method = node.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public);
|
|
object[] parameters = { null };
|
|
bool success = method != null && (bool)method.Invoke(node, parameters);
|
|
return success ? parameters[0] : null;
|
|
}
|
|
|
|
private static object FindBlackboardVariableModel(string variableName)
|
|
{
|
|
UnityEngine.Object blackboardAsset = AssetDatabase.LoadAllAssetsAtPath(GraphAssetPath)
|
|
.FirstOrDefault(asset => asset != null && asset.GetType().Name.Contains("BehaviorBlackboardAuthoringAsset", StringComparison.Ordinal));
|
|
|
|
if (blackboardAsset == null)
|
|
return null;
|
|
|
|
PropertyInfo variablesProperty = blackboardAsset.GetType().GetProperty("Variables", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
FieldInfo variablesField = blackboardAsset.GetType().GetField("m_Variables", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
IEnumerable variables = variablesProperty?.GetValue(blackboardAsset) as IEnumerable ?? variablesField?.GetValue(blackboardAsset) as IEnumerable;
|
|
if (variables == null)
|
|
return null;
|
|
|
|
foreach (object variable in variables)
|
|
{
|
|
if (variable == null)
|
|
continue;
|
|
|
|
PropertyInfo nameProperty = variable.GetType().GetProperty("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
FieldInfo nameField = variable.GetType().GetField("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
string name = nameProperty?.GetValue(variable) as string ?? nameField?.GetValue(variable) as string;
|
|
if (name == variableName)
|
|
return variable;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static void LinkTarget(object node, object targetVariable)
|
|
{
|
|
LinkFieldToVariable(node, "Target", typeof(GameObject), targetVariable);
|
|
}
|
|
|
|
private static void LinkFieldToVariable(object node, string fieldName, Type fieldType, object variableModel)
|
|
{
|
|
MethodInfo getVariableLinkMethod = node.GetType().GetMethod("GetVariableLink", BindingFlags.Instance | BindingFlags.Public);
|
|
object variableLink = getVariableLinkMethod?.Invoke(node, new object[] { fieldName, fieldType });
|
|
if (variableLink != null)
|
|
{
|
|
PropertyInfo blackboardVariableProperty = variableLink.GetType().GetProperty("BlackboardVariable", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
blackboardVariableProperty?.SetValue(variableLink, variableModel);
|
|
}
|
|
|
|
object fieldModel = FindFieldModel(node, fieldName);
|
|
if (fieldModel == null)
|
|
return;
|
|
|
|
PropertyInfo linkedVariableProperty = fieldModel.GetType().GetProperty("LinkedVariable", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
FieldInfo linkedVariableField = fieldModel.GetType().GetField("LinkedVariable", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
|
|
if (linkedVariableProperty != null && linkedVariableProperty.CanWrite)
|
|
{
|
|
linkedVariableProperty.SetValue(fieldModel, variableModel);
|
|
}
|
|
else
|
|
{
|
|
linkedVariableField?.SetValue(fieldModel, variableModel);
|
|
}
|
|
}
|
|
|
|
private static object FindFieldModel(object node, string fieldName)
|
|
{
|
|
FieldInfo fieldValuesField = node.GetType().GetField("m_FieldValues", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
PropertyInfo fieldValuesProperty = node.GetType().GetProperty("FieldValues", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
|
|
IEnumerable fieldValues = fieldValuesField?.GetValue(node) as IEnumerable ?? fieldValuesProperty?.GetValue(node) as IEnumerable;
|
|
if (fieldValues == null)
|
|
return null;
|
|
|
|
foreach (object fieldModel in fieldValues)
|
|
{
|
|
if (fieldModel == null)
|
|
continue;
|
|
|
|
PropertyInfo fieldNameProperty = fieldModel.GetType().GetProperty("FieldName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
FieldInfo fieldNameField = fieldModel.GetType().GetField("FieldName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
string currentFieldName = fieldNameProperty?.GetValue(fieldModel) as string ?? fieldNameField?.GetValue(fieldModel) as string;
|
|
if (currentFieldName == fieldName)
|
|
return fieldModel;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static void SetStartRepeatFlags(object startNode, bool repeat, bool allowMultipleRepeatsPerTick)
|
|
{
|
|
Type startNodeType = startNode.GetType();
|
|
FieldInfo repeatField = startNodeType.GetField("Repeat", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
FieldInfo allowField = startNodeType.GetField("AllowMultipleRepeatsPerTick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
|
|
repeatField?.SetValue(startNode, repeat);
|
|
allowField?.SetValue(startNode, allowMultipleRepeatsPerTick);
|
|
}
|
|
}
|
|
}
|