Use StateRootNodeIndex for per-state sub-graph lookup instead of name matching

- StateMachineMetadata: store root node property names per state via StateRootPropNames
- AssociateStateMachineNames: capture root prop name from StateRootNodeIndex
- BuildStateMachineOverviewLayers: store StateRootNodeName on overview state nodes
- TryOpenSubGraph: find per-state layer by root node reference, not name path

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-03 12:35:43 +00:00
parent 2711be76e0
commit 5b4cb7e4ee
2 changed files with 34 additions and 9 deletions

View File

@ -60,6 +60,7 @@ internal class StateMachineMetadata
{
public string MachineName { get; init; } = string.Empty;
public List<string> StateNames { get; } = [];
public List<string> StateRootPropNames { get; } = [];
public List<(int PreviousState, int NextState)> Transitions { get; } = [];
}
@ -342,6 +343,9 @@ public class AnimGraphViewModel
ExportType = "State",
IsStateMachineState = true
};
// Store root node property name for StateRootNodeIndex-based lookup
if (i < sm.StateRootPropNames.Count && !string.IsNullOrEmpty(sm.StateRootPropNames[i]))
stateNode.AdditionalProperties["StateRootNodeName"] = sm.StateRootPropNames[i];
stateNode.Pins.Add(new AnimGraphPin
{
PinName = "In",
@ -535,6 +539,7 @@ public class AnimGraphViewModel
if (states.Properties[stateIdx].GetValue(typeof(FStructFallback)) is not FStructFallback stateStruct)
{
metadata.StateNames.Add($"State_{stateIdx}");
metadata.StateRootPropNames.Add(string.Empty);
continue;
}
@ -545,14 +550,16 @@ public class AnimGraphViewModel
metadata.StateNames.Add(stateName);
// Mark root node
if (!stateStruct.TryGetValue(out int stateRootIndex, "StateRootNodeIndex"))
continue;
if (stateRootIndex < 0 || stateRootIndex >= animNodeProps.Count)
// Mark root node via StateRootNodeIndex
if (!stateStruct.TryGetValue(out int stateRootIndex, "StateRootNodeIndex") ||
stateRootIndex < 0 || stateRootIndex >= animNodeProps.Count)
{
metadata.StateRootPropNames.Add(string.Empty);
continue;
}
var rootPropName = animNodeProps[stateRootIndex].name;
metadata.StateRootPropNames.Add(rootPropName);
if (nodeByName.TryGetValue(rootPropName, out var rootNode))
rootNode.AdditionalProperties["BelongsToStateMachine"] = machineName;
}

View File

@ -563,10 +563,28 @@ public partial class AnimGraphViewer
}
else if (node.IsStateMachineState)
{
// State nodes within an overview: open the per-state internal sub-graph
// Per-state layers are named "overviewPath > stateName"
var overviewName = _currentLayerState?.Layer.Name ?? string.Empty;
layerName = $"{overviewName}{AnimGraphViewModel.SubGraphPathSeparator}{node.Name}";
// State nodes within an overview: find the per-state layer by StateRootNodeIndex
// The root node's property name is stored on the overview state node
if (node.AdditionalProperties.TryGetValue("StateRootNodeName", out var rootNodeName) &&
!string.IsNullOrEmpty(rootNodeName))
{
var targetLayer = _viewModel.Layers.FirstOrDefault(l =>
l.Nodes.Any(n => n.Name == rootNodeName));
if (targetLayer != null)
{
// If tab already exists, just select it
foreach (System.Windows.Controls.TabItem tab in LayerTabControl.Items)
{
if (tab.Tag == targetLayer)
{
LayerTabControl.SelectedItem = tab;
return;
}
}
AddLayerTab(targetLayer);
return;
}
}
}
else if (node.ExportType.Contains("StateMachine", StringComparison.OrdinalIgnoreCase))
{