ue-mcp 0.4.27 → 0.4.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,13 @@ export const animationTool = categoryTool("animation", "Animation assets, skelet
19
19
  get_bone_transforms: bp("get_bone_transforms"),
20
20
  set_montage_sequence: bp("set_montage_sequence"),
21
21
  set_montage_properties: bp("set_montage_properties"),
22
+ // State machine authoring
23
+ create_state_machine: bp("create_state_machine"),
24
+ add_state: bp("add_state"),
25
+ add_transition: bp("add_transition"),
26
+ set_state_animation: bp("set_state_animation"),
27
+ set_transition_blend: bp("set_transition_blend"),
28
+ read_state_machine: bp("read_state_machine"),
22
29
  }, `- read_anim_blueprint: Read AnimBP structure. Params: assetPath
23
30
  - read_montage: Read montage. Params: assetPath
24
31
  - read_sequence: Read anim sequence. Params: assetPath
@@ -36,7 +43,13 @@ export const animationTool = categoryTool("animation", "Animation assets, skelet
36
43
  - set_bone_keyframes: Set bone transform keyframes. Params: assetPath, boneName, keyframes (array of {frame, location?, rotation?, scale?})
37
44
  - get_bone_transforms: Read reference pose transforms. Params: skeletonPath, boneNames? (array filter)
38
45
  - set_montage_sequence: Replace animation sequence in a montage (auto-updates montage duration). Params: assetPath, animSequencePath, slotIndex?
39
- - set_montage_properties: Set montage properties directly. Params: assetPath, sequenceLength?, rateScale?, blendIn?, blendOut?`, {
46
+ - set_montage_properties: Set montage properties directly. Params: assetPath, sequenceLength?, rateScale?, blendIn?, blendOut?
47
+ - create_state_machine: Create state machine in AnimBP AnimGraph. Params: assetPath, name?, graphName?
48
+ - add_state: Add state to a state machine. Params: assetPath, stateMachineName, stateName
49
+ - add_transition: Add directed transition between states. Params: assetPath, stateMachineName, fromState, toState
50
+ - set_state_animation: Assign anim asset to state. Params: assetPath, stateMachineName, stateName, animAssetPath
51
+ - set_transition_blend: Set blend type/duration on transition. Params: assetPath, stateMachineName, fromState, toState, blendDuration?, blendLogic? (Standard|Inertialization)
52
+ - read_state_machine: Read state machine topology. Params: assetPath, stateMachineName`, {
40
53
  assetPath: z.string().optional(),
41
54
  directory: z.string().optional(),
42
55
  recursive: z.boolean().optional(),
@@ -59,6 +72,15 @@ export const animationTool = categoryTool("animation", "Animation assets, skelet
59
72
  boneName: z.string().optional(),
60
73
  boneNames: z.array(z.string()).optional(),
61
74
  parentClass: z.string().optional().describe("Parent AnimInstance class name for create_anim_blueprint"),
75
+ // State machine params
76
+ stateMachineName: z.string().optional(),
77
+ stateName: z.string().optional(),
78
+ fromState: z.string().optional(),
79
+ toState: z.string().optional(),
80
+ animAssetPath: z.string().optional().describe("Path to animation sequence or blendspace"),
81
+ blendDuration: z.number().optional(),
82
+ blendLogic: z.string().optional().describe("Standard or Inertialization"),
83
+ graphName: z.string().optional().describe("Target graph name (default: AnimGraph)"),
62
84
  keyframes: z.array(z.object({
63
85
  frame: z.number(),
64
86
  location: z.object({ x: z.number(), y: z.number(), z: z.number() }).optional(),
@@ -1 +1 @@
1
- {"version":3,"file":"animation.js","sourceRoot":"","sources":["../../src/tools/animation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"animation.js","sourceRoot":"","sources":["../../src/tools/animation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ue-mcp",
3
- "version": "0.4.27",
3
+ "version": "0.4.28",
4
4
  "description": "Unreal Engine MCP server — 19 tools, 300+ actions for AI-driven editor control",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,6 +29,18 @@
29
29
  #include "Animation/AnimData/IAnimationDataModel.h"
30
30
  #include "Editor.h"
31
31
 
32
+ // State machine authoring
33
+ #include "AnimGraphNode_StateMachine.h"
34
+ #include "AnimStateNode.h"
35
+ #include "AnimStateTransitionNode.h"
36
+ #include "AnimStateEntryNode.h"
37
+ #include "AnimationStateMachineGraph.h"
38
+ #include "AnimGraphNode_SequencePlayer.h"
39
+ #include "AnimGraphNode_BlendSpacePlayer.h"
40
+ #include "AnimGraphNode_Inertialization.h"
41
+ #include "Kismet2/BlueprintEditorUtils.h"
42
+ #include "Kismet2/KismetEditorUtilities.h"
43
+
32
44
  void FAnimationHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
33
45
  {
34
46
  Registry.RegisterHandler(TEXT("list_anim_assets"), &ListAnimAssets);
@@ -51,6 +63,14 @@ void FAnimationHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
51
63
  Registry.RegisterHandler(TEXT("get_bone_transforms"), &GetBoneTransforms);
52
64
  Registry.RegisterHandler(TEXT("set_montage_sequence"), &SetMontageSequence);
53
65
  Registry.RegisterHandler(TEXT("set_montage_properties"), &SetMontageProperties);
66
+
67
+ // State machine authoring
68
+ Registry.RegisterHandler(TEXT("create_state_machine"), &CreateStateMachine);
69
+ Registry.RegisterHandler(TEXT("add_state"), &AddState);
70
+ Registry.RegisterHandler(TEXT("add_transition"), &AddTransition);
71
+ Registry.RegisterHandler(TEXT("set_state_animation"), &SetStateAnimation);
72
+ Registry.RegisterHandler(TEXT("set_transition_blend"), &SetTransitionBlend);
73
+ Registry.RegisterHandler(TEXT("read_state_machine"), &ReadStateMachine);
54
74
  }
55
75
 
56
76
  TSharedPtr<FJsonValue> FAnimationHandlers::ListAnimAssets(const TSharedPtr<FJsonObject>& Params)
@@ -1566,3 +1586,644 @@ TSharedPtr<FJsonValue> FAnimationHandlers::SetMontageProperties(const TSharedPtr
1566
1586
 
1567
1587
  return MakeShared<FJsonValueObject>(Result);
1568
1588
  }
1589
+
1590
+ // ─── State Machine Helpers ────────────────────────────────────────
1591
+
1592
+ static UAnimBlueprint* LoadAnimBP(const FString& Path)
1593
+ {
1594
+ return LoadObject<UAnimBlueprint>(nullptr, *Path);
1595
+ }
1596
+
1597
+ static UEdGraph* FindGraphByName(UBlueprint* BP, const FString& Name)
1598
+ {
1599
+ TArray<UEdGraph*> All;
1600
+ BP->GetAllGraphs(All);
1601
+ for (UEdGraph* G : All)
1602
+ {
1603
+ if (G && G->GetName() == Name) return G;
1604
+ }
1605
+ return nullptr;
1606
+ }
1607
+
1608
+ // Find the SM container node (UAnimGraphNode_StateMachine) by its machine name
1609
+ static UAnimGraphNode_StateMachine* FindStateMachineNode(UBlueprint* BP, const FString& MachineName)
1610
+ {
1611
+ TArray<UEdGraph*> All;
1612
+ BP->GetAllGraphs(All);
1613
+ for (UEdGraph* G : All)
1614
+ {
1615
+ for (UEdGraphNode* Node : G->Nodes)
1616
+ {
1617
+ if (UAnimGraphNode_StateMachine* SM = Cast<UAnimGraphNode_StateMachine>(Node))
1618
+ {
1619
+ if (UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SM->EditorStateMachineGraph))
1620
+ {
1621
+ if (SMGraph->GetName() == MachineName || SM->GetNodeTitle(ENodeTitleType::FullTitle).ToString().Contains(MachineName))
1622
+ {
1623
+ return SM;
1624
+ }
1625
+ }
1626
+ }
1627
+ }
1628
+ }
1629
+ return nullptr;
1630
+ }
1631
+
1632
+ // Find a state node by name within a state machine graph
1633
+ static UAnimStateNode* FindStateNode(UAnimationStateMachineGraph* SMGraph, const FString& StateName)
1634
+ {
1635
+ for (UEdGraphNode* Node : SMGraph->Nodes)
1636
+ {
1637
+ if (UAnimStateNode* State = Cast<UAnimStateNode>(Node))
1638
+ {
1639
+ if (State->GetStateName() == StateName)
1640
+ {
1641
+ return State;
1642
+ }
1643
+ }
1644
+ }
1645
+ return nullptr;
1646
+ }
1647
+
1648
+ static void CompileAndSave(UBlueprint* BP)
1649
+ {
1650
+ FKismetEditorUtilities::CompileBlueprint(BP);
1651
+ UPackage* Package = BP->GetOutermost();
1652
+ if (Package)
1653
+ {
1654
+ Package->MarkPackageDirty();
1655
+ FString FileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1656
+ FSavePackageArgs SaveArgs;
1657
+ SaveArgs.TopLevelFlags = RF_Standalone;
1658
+ UPackage::SavePackage(Package, nullptr, *FileName, SaveArgs);
1659
+ }
1660
+ }
1661
+
1662
+ // ─── State Machine Handlers ──────────────────────────────────────
1663
+
1664
+ TSharedPtr<FJsonValue> FAnimationHandlers::CreateStateMachine(const TSharedPtr<FJsonObject>& Params)
1665
+ {
1666
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1667
+
1668
+ FString AssetPath;
1669
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
1670
+ {
1671
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
1672
+ Result->SetBoolField(TEXT("success"), false);
1673
+ return MakeShared<FJsonValueObject>(Result);
1674
+ }
1675
+
1676
+ FString Name = TEXT("NewStateMachine");
1677
+ Params->TryGetStringField(TEXT("name"), Name);
1678
+
1679
+ FString GraphName = TEXT("AnimGraph");
1680
+ Params->TryGetStringField(TEXT("graphName"), GraphName);
1681
+
1682
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
1683
+ if (!AnimBP)
1684
+ {
1685
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
1686
+ Result->SetBoolField(TEXT("success"), false);
1687
+ return MakeShared<FJsonValueObject>(Result);
1688
+ }
1689
+
1690
+ UEdGraph* TargetGraph = FindGraphByName(AnimBP, GraphName);
1691
+ if (!TargetGraph)
1692
+ {
1693
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Graph not found: %s"), *GraphName));
1694
+ Result->SetBoolField(TEXT("success"), false);
1695
+ return MakeShared<FJsonValueObject>(Result);
1696
+ }
1697
+
1698
+ // Create the state machine container node in the AnimGraph
1699
+ UAnimGraphNode_StateMachine* SMNode = NewObject<UAnimGraphNode_StateMachine>(TargetGraph);
1700
+ SMNode->GetStateMachineNode()->SetStateMachineName(FName(*Name));
1701
+ TargetGraph->AddNode(SMNode, false, false);
1702
+ SMNode->CreateNewGuid();
1703
+ SMNode->PostPlacedNewNode();
1704
+ SMNode->AllocateDefaultPins();
1705
+ SMNode->NodePosX = 200;
1706
+ SMNode->NodePosY = 0;
1707
+
1708
+ CompileAndSave(AnimBP);
1709
+
1710
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
1711
+ Result->SetStringField(TEXT("name"), Name);
1712
+ Result->SetStringField(TEXT("graphName"), GraphName);
1713
+ Result->SetBoolField(TEXT("success"), true);
1714
+
1715
+ return MakeShared<FJsonValueObject>(Result);
1716
+ }
1717
+
1718
+ TSharedPtr<FJsonValue> FAnimationHandlers::AddState(const TSharedPtr<FJsonObject>& Params)
1719
+ {
1720
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1721
+
1722
+ FString AssetPath;
1723
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
1724
+ {
1725
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
1726
+ Result->SetBoolField(TEXT("success"), false);
1727
+ return MakeShared<FJsonValueObject>(Result);
1728
+ }
1729
+
1730
+ FString SMName;
1731
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName))
1732
+ {
1733
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateMachineName'"));
1734
+ Result->SetBoolField(TEXT("success"), false);
1735
+ return MakeShared<FJsonValueObject>(Result);
1736
+ }
1737
+
1738
+ FString StateName;
1739
+ if (!Params->TryGetStringField(TEXT("stateName"), StateName))
1740
+ {
1741
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateName'"));
1742
+ Result->SetBoolField(TEXT("success"), false);
1743
+ return MakeShared<FJsonValueObject>(Result);
1744
+ }
1745
+
1746
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
1747
+ if (!AnimBP)
1748
+ {
1749
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
1750
+ Result->SetBoolField(TEXT("success"), false);
1751
+ return MakeShared<FJsonValueObject>(Result);
1752
+ }
1753
+
1754
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
1755
+ if (!SMNode)
1756
+ {
1757
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
1758
+ Result->SetBoolField(TEXT("success"), false);
1759
+ return MakeShared<FJsonValueObject>(Result);
1760
+ }
1761
+
1762
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
1763
+ if (!SMGraph)
1764
+ {
1765
+ Result->SetStringField(TEXT("error"), TEXT("State machine has no editor graph"));
1766
+ Result->SetBoolField(TEXT("success"), false);
1767
+ return MakeShared<FJsonValueObject>(Result);
1768
+ }
1769
+
1770
+ // Check for duplicate
1771
+ if (FindStateNode(SMGraph, StateName))
1772
+ {
1773
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' already exists"), *StateName));
1774
+ Result->SetBoolField(TEXT("success"), false);
1775
+ return MakeShared<FJsonValueObject>(Result);
1776
+ }
1777
+
1778
+ // Create state node
1779
+ UAnimStateNode* NewState = NewObject<UAnimStateNode>(SMGraph);
1780
+ SMGraph->AddNode(NewState, false, false);
1781
+ NewState->CreateNewGuid();
1782
+ NewState->PostPlacedNewNode();
1783
+ NewState->AllocateDefaultPins();
1784
+
1785
+ // Set the state name via the BoundGraph (the state's internal graph)
1786
+ if (NewState->BoundGraph)
1787
+ {
1788
+ NewState->BoundGraph->Rename(*StateName);
1789
+ }
1790
+
1791
+ // Position states in a grid
1792
+ int32 StateCount = 0;
1793
+ for (UEdGraphNode* N : SMGraph->Nodes) { if (Cast<UAnimStateNode>(N)) StateCount++; }
1794
+ NewState->NodePosX = 300 + ((StateCount - 1) % 4) * 300;
1795
+ NewState->NodePosY = ((StateCount - 1) / 4) * 200;
1796
+
1797
+ CompileAndSave(AnimBP);
1798
+
1799
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
1800
+ Result->SetStringField(TEXT("stateMachineName"), SMName);
1801
+ Result->SetStringField(TEXT("stateName"), StateName);
1802
+ Result->SetBoolField(TEXT("success"), true);
1803
+
1804
+ return MakeShared<FJsonValueObject>(Result);
1805
+ }
1806
+
1807
+ TSharedPtr<FJsonValue> FAnimationHandlers::AddTransition(const TSharedPtr<FJsonObject>& Params)
1808
+ {
1809
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1810
+
1811
+ FString AssetPath;
1812
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
1813
+ {
1814
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
1815
+ Result->SetBoolField(TEXT("success"), false);
1816
+ return MakeShared<FJsonValueObject>(Result);
1817
+ }
1818
+
1819
+ FString SMName;
1820
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName))
1821
+ {
1822
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateMachineName'"));
1823
+ Result->SetBoolField(TEXT("success"), false);
1824
+ return MakeShared<FJsonValueObject>(Result);
1825
+ }
1826
+
1827
+ FString FromState, ToState;
1828
+ if (!Params->TryGetStringField(TEXT("fromState"), FromState))
1829
+ {
1830
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'fromState'"));
1831
+ Result->SetBoolField(TEXT("success"), false);
1832
+ return MakeShared<FJsonValueObject>(Result);
1833
+ }
1834
+ if (!Params->TryGetStringField(TEXT("toState"), ToState))
1835
+ {
1836
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'toState'"));
1837
+ Result->SetBoolField(TEXT("success"), false);
1838
+ return MakeShared<FJsonValueObject>(Result);
1839
+ }
1840
+
1841
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
1842
+ if (!AnimBP)
1843
+ {
1844
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
1845
+ Result->SetBoolField(TEXT("success"), false);
1846
+ return MakeShared<FJsonValueObject>(Result);
1847
+ }
1848
+
1849
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
1850
+ if (!SMNode)
1851
+ {
1852
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
1853
+ Result->SetBoolField(TEXT("success"), false);
1854
+ return MakeShared<FJsonValueObject>(Result);
1855
+ }
1856
+
1857
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
1858
+ UAnimStateNode* From = FindStateNode(SMGraph, FromState);
1859
+ UAnimStateNode* To = FindStateNode(SMGraph, ToState);
1860
+ if (!From)
1861
+ {
1862
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' not found"), *FromState));
1863
+ Result->SetBoolField(TEXT("success"), false);
1864
+ return MakeShared<FJsonValueObject>(Result);
1865
+ }
1866
+ if (!To)
1867
+ {
1868
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' not found"), *ToState));
1869
+ Result->SetBoolField(TEXT("success"), false);
1870
+ return MakeShared<FJsonValueObject>(Result);
1871
+ }
1872
+
1873
+ // Create transition node
1874
+ UAnimStateTransitionNode* TransNode = NewObject<UAnimStateTransitionNode>(SMGraph);
1875
+ SMGraph->AddNode(TransNode, false, false);
1876
+ TransNode->CreateNewGuid();
1877
+ TransNode->PostPlacedNewNode();
1878
+ TransNode->AllocateDefaultPins();
1879
+
1880
+ // Position between the two states
1881
+ TransNode->NodePosX = (From->NodePosX + To->NodePosX) / 2;
1882
+ TransNode->NodePosY = (From->NodePosY + To->NodePosY) / 2;
1883
+
1884
+ // Wire: From output → Transition input, Transition output → To input
1885
+ UEdGraphPin* FromOut = From->GetOutputPin();
1886
+ UEdGraphPin* TransIn = TransNode->GetInputPin();
1887
+ UEdGraphPin* TransOut = TransNode->GetOutputPin();
1888
+ UEdGraphPin* ToIn = To->GetInputPin();
1889
+
1890
+ if (FromOut && TransIn)
1891
+ {
1892
+ FromOut->MakeLinkTo(TransIn);
1893
+ }
1894
+ if (TransOut && ToIn)
1895
+ {
1896
+ TransOut->MakeLinkTo(ToIn);
1897
+ }
1898
+
1899
+ CompileAndSave(AnimBP);
1900
+
1901
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
1902
+ Result->SetStringField(TEXT("stateMachineName"), SMName);
1903
+ Result->SetStringField(TEXT("fromState"), FromState);
1904
+ Result->SetStringField(TEXT("toState"), ToState);
1905
+ Result->SetBoolField(TEXT("success"), true);
1906
+
1907
+ return MakeShared<FJsonValueObject>(Result);
1908
+ }
1909
+
1910
+ TSharedPtr<FJsonValue> FAnimationHandlers::SetStateAnimation(const TSharedPtr<FJsonObject>& Params)
1911
+ {
1912
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1913
+
1914
+ FString AssetPath;
1915
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
1916
+ {
1917
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
1918
+ Result->SetBoolField(TEXT("success"), false);
1919
+ return MakeShared<FJsonValueObject>(Result);
1920
+ }
1921
+
1922
+ FString SMName, StateName, AnimAssetPath;
1923
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName) ||
1924
+ !Params->TryGetStringField(TEXT("stateName"), StateName) ||
1925
+ !Params->TryGetStringField(TEXT("animAssetPath"), AnimAssetPath))
1926
+ {
1927
+ Result->SetStringField(TEXT("error"), TEXT("Missing required params: stateMachineName, stateName, animAssetPath"));
1928
+ Result->SetBoolField(TEXT("success"), false);
1929
+ return MakeShared<FJsonValueObject>(Result);
1930
+ }
1931
+
1932
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
1933
+ if (!AnimBP)
1934
+ {
1935
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
1936
+ Result->SetBoolField(TEXT("success"), false);
1937
+ return MakeShared<FJsonValueObject>(Result);
1938
+ }
1939
+
1940
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
1941
+ if (!SMNode)
1942
+ {
1943
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
1944
+ Result->SetBoolField(TEXT("success"), false);
1945
+ return MakeShared<FJsonValueObject>(Result);
1946
+ }
1947
+
1948
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
1949
+ UAnimStateNode* State = FindStateNode(SMGraph, StateName);
1950
+ if (!State)
1951
+ {
1952
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' not found"), *StateName));
1953
+ Result->SetBoolField(TEXT("success"), false);
1954
+ return MakeShared<FJsonValueObject>(Result);
1955
+ }
1956
+
1957
+ // Load the animation asset
1958
+ UAnimationAsset* AnimAsset = LoadObject<UAnimationAsset>(nullptr, *AnimAssetPath);
1959
+ if (!AnimAsset)
1960
+ {
1961
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Animation asset not found: %s"), *AnimAssetPath));
1962
+ Result->SetBoolField(TEXT("success"), false);
1963
+ return MakeShared<FJsonValueObject>(Result);
1964
+ }
1965
+
1966
+ // Get the state's bound graph (internal graph that plays the animation)
1967
+ UEdGraph* StateGraph = State->BoundGraph;
1968
+ if (!StateGraph)
1969
+ {
1970
+ Result->SetStringField(TEXT("error"), TEXT("State has no BoundGraph"));
1971
+ Result->SetBoolField(TEXT("success"), false);
1972
+ return MakeShared<FJsonValueObject>(Result);
1973
+ }
1974
+
1975
+ // Find or create the appropriate player node inside the state graph
1976
+ // Look for existing sequence player or blendspace player
1977
+ UAnimGraphNode_SequencePlayer* SeqPlayer = nullptr;
1978
+ UAnimGraphNode_BlendSpacePlayer* BSPlayer = nullptr;
1979
+ for (UEdGraphNode* Node : StateGraph->Nodes)
1980
+ {
1981
+ if (!SeqPlayer) SeqPlayer = Cast<UAnimGraphNode_SequencePlayer>(Node);
1982
+ if (!BSPlayer) BSPlayer = Cast<UAnimGraphNode_BlendSpacePlayer>(Node);
1983
+ }
1984
+
1985
+ if (UAnimSequence* Seq = Cast<UAnimSequence>(AnimAsset))
1986
+ {
1987
+ if (!SeqPlayer)
1988
+ {
1989
+ SeqPlayer = NewObject<UAnimGraphNode_SequencePlayer>(StateGraph);
1990
+ StateGraph->AddNode(SeqPlayer, false, false);
1991
+ SeqPlayer->CreateNewGuid();
1992
+ SeqPlayer->PostPlacedNewNode();
1993
+ SeqPlayer->AllocateDefaultPins();
1994
+ }
1995
+ SeqPlayer->GetSequencePlayerNode()->SetSequence(Seq);
1996
+ }
1997
+ else if (UBlendSpace* BS = Cast<UBlendSpace>(AnimAsset))
1998
+ {
1999
+ if (!BSPlayer)
2000
+ {
2001
+ BSPlayer = NewObject<UAnimGraphNode_BlendSpacePlayer>(StateGraph);
2002
+ StateGraph->AddNode(BSPlayer, false, false);
2003
+ BSPlayer->CreateNewGuid();
2004
+ BSPlayer->PostPlacedNewNode();
2005
+ BSPlayer->AllocateDefaultPins();
2006
+ }
2007
+ BSPlayer->GetBlendSpacePlayerNode()->SetBlendSpace(BS);
2008
+ }
2009
+ else
2010
+ {
2011
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Unsupported animation asset type: %s"), *AnimAsset->GetClass()->GetName()));
2012
+ Result->SetBoolField(TEXT("success"), false);
2013
+ return MakeShared<FJsonValueObject>(Result);
2014
+ }
2015
+
2016
+ CompileAndSave(AnimBP);
2017
+
2018
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
2019
+ Result->SetStringField(TEXT("stateName"), StateName);
2020
+ Result->SetStringField(TEXT("animAssetPath"), AnimAssetPath);
2021
+ Result->SetBoolField(TEXT("success"), true);
2022
+
2023
+ return MakeShared<FJsonValueObject>(Result);
2024
+ }
2025
+
2026
+ TSharedPtr<FJsonValue> FAnimationHandlers::SetTransitionBlend(const TSharedPtr<FJsonObject>& Params)
2027
+ {
2028
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
2029
+
2030
+ FString AssetPath;
2031
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
2032
+ {
2033
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
2034
+ Result->SetBoolField(TEXT("success"), false);
2035
+ return MakeShared<FJsonValueObject>(Result);
2036
+ }
2037
+
2038
+ FString SMName, FromState, ToState;
2039
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName) ||
2040
+ !Params->TryGetStringField(TEXT("fromState"), FromState) ||
2041
+ !Params->TryGetStringField(TEXT("toState"), ToState))
2042
+ {
2043
+ Result->SetStringField(TEXT("error"), TEXT("Missing required params: stateMachineName, fromState, toState"));
2044
+ Result->SetBoolField(TEXT("success"), false);
2045
+ return MakeShared<FJsonValueObject>(Result);
2046
+ }
2047
+
2048
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
2049
+ if (!AnimBP)
2050
+ {
2051
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
2052
+ Result->SetBoolField(TEXT("success"), false);
2053
+ return MakeShared<FJsonValueObject>(Result);
2054
+ }
2055
+
2056
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
2057
+ if (!SMNode)
2058
+ {
2059
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
2060
+ Result->SetBoolField(TEXT("success"), false);
2061
+ return MakeShared<FJsonValueObject>(Result);
2062
+ }
2063
+
2064
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
2065
+
2066
+ // Find the transition between fromState and toState
2067
+ UAnimStateTransitionNode* TransNode = nullptr;
2068
+ for (UEdGraphNode* Node : SMGraph->Nodes)
2069
+ {
2070
+ if (UAnimStateTransitionNode* T = Cast<UAnimStateTransitionNode>(Node))
2071
+ {
2072
+ UAnimStateNode* Prev = Cast<UAnimStateNode>(T->GetPreviousState());
2073
+ UAnimStateNode* Next = Cast<UAnimStateNode>(T->GetNextState());
2074
+ if (Prev && Next && Prev->GetStateName() == FromState && Next->GetStateName() == ToState)
2075
+ {
2076
+ TransNode = T;
2077
+ break;
2078
+ }
2079
+ }
2080
+ }
2081
+
2082
+ if (!TransNode)
2083
+ {
2084
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("No transition from '%s' to '%s'"), *FromState, *ToState));
2085
+ Result->SetBoolField(TEXT("success"), false);
2086
+ return MakeShared<FJsonValueObject>(Result);
2087
+ }
2088
+
2089
+ // Set blend duration
2090
+ double BlendDuration = 0.2;
2091
+ if (Params->TryGetNumberField(TEXT("blendDuration"), BlendDuration))
2092
+ {
2093
+ TransNode->CrossfadeDuration = static_cast<float>(BlendDuration);
2094
+ }
2095
+
2096
+ // Set blend logic (Standard vs Inertialization)
2097
+ FString BlendLogic;
2098
+ if (Params->TryGetStringField(TEXT("blendLogic"), BlendLogic))
2099
+ {
2100
+ if (BlendLogic.Equals(TEXT("Inertialization"), ESearchCase::IgnoreCase))
2101
+ {
2102
+ TransNode->BlendMode = EAlphaBlendOption::Linear;
2103
+ TransNode->LogicType = ETransitionLogicType::TLT_Inertialization;
2104
+ }
2105
+ else // Standard
2106
+ {
2107
+ TransNode->LogicType = ETransitionLogicType::TLT_StandardBlend;
2108
+ }
2109
+ }
2110
+
2111
+ CompileAndSave(AnimBP);
2112
+
2113
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
2114
+ Result->SetStringField(TEXT("fromState"), FromState);
2115
+ Result->SetStringField(TEXT("toState"), ToState);
2116
+ Result->SetNumberField(TEXT("blendDuration"), BlendDuration);
2117
+ Result->SetBoolField(TEXT("success"), true);
2118
+
2119
+ return MakeShared<FJsonValueObject>(Result);
2120
+ }
2121
+
2122
+ TSharedPtr<FJsonValue> FAnimationHandlers::ReadStateMachine(const TSharedPtr<FJsonObject>& Params)
2123
+ {
2124
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
2125
+
2126
+ FString AssetPath;
2127
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
2128
+ {
2129
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
2130
+ Result->SetBoolField(TEXT("success"), false);
2131
+ return MakeShared<FJsonValueObject>(Result);
2132
+ }
2133
+
2134
+ FString SMName;
2135
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName))
2136
+ {
2137
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateMachineName'"));
2138
+ Result->SetBoolField(TEXT("success"), false);
2139
+ return MakeShared<FJsonValueObject>(Result);
2140
+ }
2141
+
2142
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
2143
+ if (!AnimBP)
2144
+ {
2145
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
2146
+ Result->SetBoolField(TEXT("success"), false);
2147
+ return MakeShared<FJsonValueObject>(Result);
2148
+ }
2149
+
2150
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
2151
+ if (!SMNode)
2152
+ {
2153
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
2154
+ Result->SetBoolField(TEXT("success"), false);
2155
+ return MakeShared<FJsonValueObject>(Result);
2156
+ }
2157
+
2158
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
2159
+ if (!SMGraph)
2160
+ {
2161
+ Result->SetStringField(TEXT("error"), TEXT("State machine has no editor graph"));
2162
+ Result->SetBoolField(TEXT("success"), false);
2163
+ return MakeShared<FJsonValueObject>(Result);
2164
+ }
2165
+
2166
+ // Enumerate states
2167
+ TArray<TSharedPtr<FJsonValue>> StatesArray;
2168
+ for (UEdGraphNode* Node : SMGraph->Nodes)
2169
+ {
2170
+ if (UAnimStateNode* State = Cast<UAnimStateNode>(Node))
2171
+ {
2172
+ TSharedPtr<FJsonObject> StateObj = MakeShared<FJsonObject>();
2173
+ StateObj->SetStringField(TEXT("name"), State->GetStateName());
2174
+
2175
+ // Check for animation asset inside the state
2176
+ if (State->BoundGraph)
2177
+ {
2178
+ for (UEdGraphNode* Inner : State->BoundGraph->Nodes)
2179
+ {
2180
+ if (UAnimGraphNode_SequencePlayer* SP = Cast<UAnimGraphNode_SequencePlayer>(Inner))
2181
+ {
2182
+ if (UAnimSequence* Seq = SP->GetSequencePlayerNode()->GetSequence())
2183
+ {
2184
+ StateObj->SetStringField(TEXT("animAsset"), Seq->GetPathName());
2185
+ }
2186
+ }
2187
+ else if (UAnimGraphNode_BlendSpacePlayer* BSP = Cast<UAnimGraphNode_BlendSpacePlayer>(Inner))
2188
+ {
2189
+ if (UBlendSpace* BS = BSP->GetBlendSpacePlayerNode()->GetBlendSpace())
2190
+ {
2191
+ StateObj->SetStringField(TEXT("animAsset"), BS->GetPathName());
2192
+ }
2193
+ }
2194
+ }
2195
+ }
2196
+
2197
+ StatesArray.Add(MakeShared<FJsonValueObject>(StateObj));
2198
+ }
2199
+ }
2200
+ Result->SetArrayField(TEXT("states"), StatesArray);
2201
+
2202
+ // Enumerate transitions
2203
+ TArray<TSharedPtr<FJsonValue>> TransArray;
2204
+ for (UEdGraphNode* Node : SMGraph->Nodes)
2205
+ {
2206
+ if (UAnimStateTransitionNode* T = Cast<UAnimStateTransitionNode>(Node))
2207
+ {
2208
+ TSharedPtr<FJsonObject> TransObj = MakeShared<FJsonObject>();
2209
+
2210
+ UAnimStateNode* Prev = Cast<UAnimStateNode>(T->GetPreviousState());
2211
+ UAnimStateNode* Next = Cast<UAnimStateNode>(T->GetNextState());
2212
+ if (Prev) TransObj->SetStringField(TEXT("fromState"), Prev->GetStateName());
2213
+ if (Next) TransObj->SetStringField(TEXT("toState"), Next->GetStateName());
2214
+
2215
+ TransObj->SetNumberField(TEXT("blendDuration"), T->CrossfadeDuration);
2216
+ TransObj->SetStringField(TEXT("logicType"),
2217
+ T->LogicType == ETransitionLogicType::TLT_Inertialization ? TEXT("Inertialization") : TEXT("Standard"));
2218
+
2219
+ TransArray.Add(MakeShared<FJsonValueObject>(TransObj));
2220
+ }
2221
+ }
2222
+ Result->SetArrayField(TEXT("transitions"), TransArray);
2223
+
2224
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
2225
+ Result->SetStringField(TEXT("stateMachineName"), SMName);
2226
+ Result->SetBoolField(TEXT("success"), true);
2227
+
2228
+ return MakeShared<FJsonValueObject>(Result);
2229
+ }
@@ -41,4 +41,12 @@ private:
41
41
  // Montage editing
42
42
  static TSharedPtr<FJsonValue> SetMontageSequence(const TSharedPtr<FJsonObject>& Params);
43
43
  static TSharedPtr<FJsonValue> SetMontageProperties(const TSharedPtr<FJsonObject>& Params);
44
+
45
+ // State machine authoring
46
+ static TSharedPtr<FJsonValue> CreateStateMachine(const TSharedPtr<FJsonObject>& Params);
47
+ static TSharedPtr<FJsonValue> AddState(const TSharedPtr<FJsonObject>& Params);
48
+ static TSharedPtr<FJsonValue> AddTransition(const TSharedPtr<FJsonObject>& Params);
49
+ static TSharedPtr<FJsonValue> SetStateAnimation(const TSharedPtr<FJsonObject>& Params);
50
+ static TSharedPtr<FJsonValue> SetTransitionBlend(const TSharedPtr<FJsonObject>& Params);
51
+ static TSharedPtr<FJsonValue> ReadStateMachine(const TSharedPtr<FJsonObject>& Params);
44
52
  };
@@ -32,6 +32,7 @@ public class UE_MCP_Bridge : ModuleRules
32
32
  new string[]
33
33
  {
34
34
  "AIModule",
35
+ "AnimGraph",
35
36
  "AnimationEditor",
36
37
  "AssetRegistry",
37
38
  "AssetTools",