ue-mcp 0.4.27 → 0.4.29

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.29",
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_AssetPlayerBase.h"
39
+ #include "AnimGraphNode_SequencePlayer.h"
40
+ #include "AnimGraphNode_BlendSpacePlayer.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,642 @@ 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
+ TargetGraph->AddNode(SMNode, false, false);
1701
+ SMNode->CreateNewGuid();
1702
+ SMNode->PostPlacedNewNode(); // This creates the EditorStateMachineGraph sub-graph
1703
+ SMNode->AllocateDefaultPins();
1704
+ SMNode->NodePosX = 200;
1705
+ SMNode->NodePosY = 0;
1706
+
1707
+ // Rename the state machine graph to the desired name
1708
+ if (SMNode->EditorStateMachineGraph)
1709
+ {
1710
+ SMNode->EditorStateMachineGraph->Rename(*Name);
1711
+ }
1712
+
1713
+ CompileAndSave(AnimBP);
1714
+
1715
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
1716
+ Result->SetStringField(TEXT("name"), Name);
1717
+ Result->SetStringField(TEXT("graphName"), GraphName);
1718
+ Result->SetBoolField(TEXT("success"), true);
1719
+
1720
+ return MakeShared<FJsonValueObject>(Result);
1721
+ }
1722
+
1723
+ TSharedPtr<FJsonValue> FAnimationHandlers::AddState(const TSharedPtr<FJsonObject>& Params)
1724
+ {
1725
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1726
+
1727
+ FString AssetPath;
1728
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
1729
+ {
1730
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
1731
+ Result->SetBoolField(TEXT("success"), false);
1732
+ return MakeShared<FJsonValueObject>(Result);
1733
+ }
1734
+
1735
+ FString SMName;
1736
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName))
1737
+ {
1738
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateMachineName'"));
1739
+ Result->SetBoolField(TEXT("success"), false);
1740
+ return MakeShared<FJsonValueObject>(Result);
1741
+ }
1742
+
1743
+ FString StateName;
1744
+ if (!Params->TryGetStringField(TEXT("stateName"), StateName))
1745
+ {
1746
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateName'"));
1747
+ Result->SetBoolField(TEXT("success"), false);
1748
+ return MakeShared<FJsonValueObject>(Result);
1749
+ }
1750
+
1751
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
1752
+ if (!AnimBP)
1753
+ {
1754
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
1755
+ Result->SetBoolField(TEXT("success"), false);
1756
+ return MakeShared<FJsonValueObject>(Result);
1757
+ }
1758
+
1759
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
1760
+ if (!SMNode)
1761
+ {
1762
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
1763
+ Result->SetBoolField(TEXT("success"), false);
1764
+ return MakeShared<FJsonValueObject>(Result);
1765
+ }
1766
+
1767
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
1768
+ if (!SMGraph)
1769
+ {
1770
+ Result->SetStringField(TEXT("error"), TEXT("State machine has no editor graph"));
1771
+ Result->SetBoolField(TEXT("success"), false);
1772
+ return MakeShared<FJsonValueObject>(Result);
1773
+ }
1774
+
1775
+ // Check for duplicate
1776
+ if (FindStateNode(SMGraph, StateName))
1777
+ {
1778
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' already exists"), *StateName));
1779
+ Result->SetBoolField(TEXT("success"), false);
1780
+ return MakeShared<FJsonValueObject>(Result);
1781
+ }
1782
+
1783
+ // Create state node
1784
+ UAnimStateNode* NewState = NewObject<UAnimStateNode>(SMGraph);
1785
+ SMGraph->AddNode(NewState, false, false);
1786
+ NewState->CreateNewGuid();
1787
+ NewState->PostPlacedNewNode();
1788
+ NewState->AllocateDefaultPins();
1789
+
1790
+ // Set the state name via the BoundGraph (the state's internal graph)
1791
+ if (NewState->BoundGraph)
1792
+ {
1793
+ NewState->BoundGraph->Rename(*StateName);
1794
+ }
1795
+
1796
+ // Position states in a grid
1797
+ int32 StateCount = 0;
1798
+ for (UEdGraphNode* N : SMGraph->Nodes) { if (Cast<UAnimStateNode>(N)) StateCount++; }
1799
+ NewState->NodePosX = 300 + ((StateCount - 1) % 4) * 300;
1800
+ NewState->NodePosY = ((StateCount - 1) / 4) * 200;
1801
+
1802
+ CompileAndSave(AnimBP);
1803
+
1804
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
1805
+ Result->SetStringField(TEXT("stateMachineName"), SMName);
1806
+ Result->SetStringField(TEXT("stateName"), StateName);
1807
+ Result->SetBoolField(TEXT("success"), true);
1808
+
1809
+ return MakeShared<FJsonValueObject>(Result);
1810
+ }
1811
+
1812
+ TSharedPtr<FJsonValue> FAnimationHandlers::AddTransition(const TSharedPtr<FJsonObject>& Params)
1813
+ {
1814
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1815
+
1816
+ FString AssetPath;
1817
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
1818
+ {
1819
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
1820
+ Result->SetBoolField(TEXT("success"), false);
1821
+ return MakeShared<FJsonValueObject>(Result);
1822
+ }
1823
+
1824
+ FString SMName;
1825
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName))
1826
+ {
1827
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateMachineName'"));
1828
+ Result->SetBoolField(TEXT("success"), false);
1829
+ return MakeShared<FJsonValueObject>(Result);
1830
+ }
1831
+
1832
+ FString FromState, ToState;
1833
+ if (!Params->TryGetStringField(TEXT("fromState"), FromState))
1834
+ {
1835
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'fromState'"));
1836
+ Result->SetBoolField(TEXT("success"), false);
1837
+ return MakeShared<FJsonValueObject>(Result);
1838
+ }
1839
+ if (!Params->TryGetStringField(TEXT("toState"), ToState))
1840
+ {
1841
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'toState'"));
1842
+ Result->SetBoolField(TEXT("success"), false);
1843
+ return MakeShared<FJsonValueObject>(Result);
1844
+ }
1845
+
1846
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
1847
+ if (!AnimBP)
1848
+ {
1849
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
1850
+ Result->SetBoolField(TEXT("success"), false);
1851
+ return MakeShared<FJsonValueObject>(Result);
1852
+ }
1853
+
1854
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
1855
+ if (!SMNode)
1856
+ {
1857
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
1858
+ Result->SetBoolField(TEXT("success"), false);
1859
+ return MakeShared<FJsonValueObject>(Result);
1860
+ }
1861
+
1862
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
1863
+ UAnimStateNode* From = FindStateNode(SMGraph, FromState);
1864
+ UAnimStateNode* To = FindStateNode(SMGraph, ToState);
1865
+ if (!From)
1866
+ {
1867
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' not found"), *FromState));
1868
+ Result->SetBoolField(TEXT("success"), false);
1869
+ return MakeShared<FJsonValueObject>(Result);
1870
+ }
1871
+ if (!To)
1872
+ {
1873
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' not found"), *ToState));
1874
+ Result->SetBoolField(TEXT("success"), false);
1875
+ return MakeShared<FJsonValueObject>(Result);
1876
+ }
1877
+
1878
+ // Create transition node
1879
+ UAnimStateTransitionNode* TransNode = NewObject<UAnimStateTransitionNode>(SMGraph);
1880
+ SMGraph->AddNode(TransNode, false, false);
1881
+ TransNode->CreateNewGuid();
1882
+ TransNode->PostPlacedNewNode();
1883
+ TransNode->AllocateDefaultPins();
1884
+
1885
+ // Position between the two states
1886
+ TransNode->NodePosX = (From->NodePosX + To->NodePosX) / 2;
1887
+ TransNode->NodePosY = (From->NodePosY + To->NodePosY) / 2;
1888
+
1889
+ // Wire: From output → Transition input, Transition output → To input
1890
+ UEdGraphPin* FromOut = From->GetOutputPin();
1891
+ UEdGraphPin* TransIn = TransNode->GetInputPin();
1892
+ UEdGraphPin* TransOut = TransNode->GetOutputPin();
1893
+ UEdGraphPin* ToIn = To->GetInputPin();
1894
+
1895
+ if (FromOut && TransIn)
1896
+ {
1897
+ FromOut->MakeLinkTo(TransIn);
1898
+ }
1899
+ if (TransOut && ToIn)
1900
+ {
1901
+ TransOut->MakeLinkTo(ToIn);
1902
+ }
1903
+
1904
+ CompileAndSave(AnimBP);
1905
+
1906
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
1907
+ Result->SetStringField(TEXT("stateMachineName"), SMName);
1908
+ Result->SetStringField(TEXT("fromState"), FromState);
1909
+ Result->SetStringField(TEXT("toState"), ToState);
1910
+ Result->SetBoolField(TEXT("success"), true);
1911
+
1912
+ return MakeShared<FJsonValueObject>(Result);
1913
+ }
1914
+
1915
+ TSharedPtr<FJsonValue> FAnimationHandlers::SetStateAnimation(const TSharedPtr<FJsonObject>& Params)
1916
+ {
1917
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1918
+
1919
+ FString AssetPath;
1920
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
1921
+ {
1922
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
1923
+ Result->SetBoolField(TEXT("success"), false);
1924
+ return MakeShared<FJsonValueObject>(Result);
1925
+ }
1926
+
1927
+ FString SMName, StateName, AnimAssetPath;
1928
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName) ||
1929
+ !Params->TryGetStringField(TEXT("stateName"), StateName) ||
1930
+ !Params->TryGetStringField(TEXT("animAssetPath"), AnimAssetPath))
1931
+ {
1932
+ Result->SetStringField(TEXT("error"), TEXT("Missing required params: stateMachineName, stateName, animAssetPath"));
1933
+ Result->SetBoolField(TEXT("success"), false);
1934
+ return MakeShared<FJsonValueObject>(Result);
1935
+ }
1936
+
1937
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
1938
+ if (!AnimBP)
1939
+ {
1940
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
1941
+ Result->SetBoolField(TEXT("success"), false);
1942
+ return MakeShared<FJsonValueObject>(Result);
1943
+ }
1944
+
1945
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
1946
+ if (!SMNode)
1947
+ {
1948
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
1949
+ Result->SetBoolField(TEXT("success"), false);
1950
+ return MakeShared<FJsonValueObject>(Result);
1951
+ }
1952
+
1953
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
1954
+ UAnimStateNode* State = FindStateNode(SMGraph, StateName);
1955
+ if (!State)
1956
+ {
1957
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State '%s' not found"), *StateName));
1958
+ Result->SetBoolField(TEXT("success"), false);
1959
+ return MakeShared<FJsonValueObject>(Result);
1960
+ }
1961
+
1962
+ // Load the animation asset
1963
+ UAnimationAsset* AnimAsset = LoadObject<UAnimationAsset>(nullptr, *AnimAssetPath);
1964
+ if (!AnimAsset)
1965
+ {
1966
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Animation asset not found: %s"), *AnimAssetPath));
1967
+ Result->SetBoolField(TEXT("success"), false);
1968
+ return MakeShared<FJsonValueObject>(Result);
1969
+ }
1970
+
1971
+ // Get the state's bound graph (internal graph that plays the animation)
1972
+ UEdGraph* StateGraph = State->BoundGraph;
1973
+ if (!StateGraph)
1974
+ {
1975
+ Result->SetStringField(TEXT("error"), TEXT("State has no BoundGraph"));
1976
+ Result->SetBoolField(TEXT("success"), false);
1977
+ return MakeShared<FJsonValueObject>(Result);
1978
+ }
1979
+
1980
+ // Find or create the appropriate player node inside the state graph
1981
+ // Look for existing sequence player or blendspace player
1982
+ UAnimGraphNode_SequencePlayer* SeqPlayer = nullptr;
1983
+ UAnimGraphNode_BlendSpacePlayer* BSPlayer = nullptr;
1984
+ for (UEdGraphNode* Node : StateGraph->Nodes)
1985
+ {
1986
+ if (!SeqPlayer) SeqPlayer = Cast<UAnimGraphNode_SequencePlayer>(Node);
1987
+ if (!BSPlayer) BSPlayer = Cast<UAnimGraphNode_BlendSpacePlayer>(Node);
1988
+ }
1989
+
1990
+ if (UAnimSequence* Seq = Cast<UAnimSequence>(AnimAsset))
1991
+ {
1992
+ if (!SeqPlayer)
1993
+ {
1994
+ SeqPlayer = NewObject<UAnimGraphNode_SequencePlayer>(StateGraph);
1995
+ StateGraph->AddNode(SeqPlayer, false, false);
1996
+ SeqPlayer->CreateNewGuid();
1997
+ SeqPlayer->PostPlacedNewNode();
1998
+ SeqPlayer->AllocateDefaultPins();
1999
+ }
2000
+ SeqPlayer->SetAnimationAsset(Seq);
2001
+ }
2002
+ else if (UBlendSpace* BS = Cast<UBlendSpace>(AnimAsset))
2003
+ {
2004
+ if (!BSPlayer)
2005
+ {
2006
+ BSPlayer = NewObject<UAnimGraphNode_BlendSpacePlayer>(StateGraph);
2007
+ StateGraph->AddNode(BSPlayer, false, false);
2008
+ BSPlayer->CreateNewGuid();
2009
+ BSPlayer->PostPlacedNewNode();
2010
+ BSPlayer->AllocateDefaultPins();
2011
+ }
2012
+ BSPlayer->SetAnimationAsset(BS);
2013
+ }
2014
+ else
2015
+ {
2016
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Unsupported animation asset type: %s"), *AnimAsset->GetClass()->GetName()));
2017
+ Result->SetBoolField(TEXT("success"), false);
2018
+ return MakeShared<FJsonValueObject>(Result);
2019
+ }
2020
+
2021
+ CompileAndSave(AnimBP);
2022
+
2023
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
2024
+ Result->SetStringField(TEXT("stateName"), StateName);
2025
+ Result->SetStringField(TEXT("animAssetPath"), AnimAssetPath);
2026
+ Result->SetBoolField(TEXT("success"), true);
2027
+
2028
+ return MakeShared<FJsonValueObject>(Result);
2029
+ }
2030
+
2031
+ TSharedPtr<FJsonValue> FAnimationHandlers::SetTransitionBlend(const TSharedPtr<FJsonObject>& Params)
2032
+ {
2033
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
2034
+
2035
+ FString AssetPath;
2036
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
2037
+ {
2038
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
2039
+ Result->SetBoolField(TEXT("success"), false);
2040
+ return MakeShared<FJsonValueObject>(Result);
2041
+ }
2042
+
2043
+ FString SMName, FromState, ToState;
2044
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName) ||
2045
+ !Params->TryGetStringField(TEXT("fromState"), FromState) ||
2046
+ !Params->TryGetStringField(TEXT("toState"), ToState))
2047
+ {
2048
+ Result->SetStringField(TEXT("error"), TEXT("Missing required params: stateMachineName, fromState, toState"));
2049
+ Result->SetBoolField(TEXT("success"), false);
2050
+ return MakeShared<FJsonValueObject>(Result);
2051
+ }
2052
+
2053
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
2054
+ if (!AnimBP)
2055
+ {
2056
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
2057
+ Result->SetBoolField(TEXT("success"), false);
2058
+ return MakeShared<FJsonValueObject>(Result);
2059
+ }
2060
+
2061
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
2062
+ if (!SMNode)
2063
+ {
2064
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
2065
+ Result->SetBoolField(TEXT("success"), false);
2066
+ return MakeShared<FJsonValueObject>(Result);
2067
+ }
2068
+
2069
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
2070
+
2071
+ // Find the transition between fromState and toState
2072
+ UAnimStateTransitionNode* TransNode = nullptr;
2073
+ for (UEdGraphNode* Node : SMGraph->Nodes)
2074
+ {
2075
+ if (UAnimStateTransitionNode* T = Cast<UAnimStateTransitionNode>(Node))
2076
+ {
2077
+ UAnimStateNode* Prev = Cast<UAnimStateNode>(T->GetPreviousState());
2078
+ UAnimStateNode* Next = Cast<UAnimStateNode>(T->GetNextState());
2079
+ if (Prev && Next && Prev->GetStateName() == FromState && Next->GetStateName() == ToState)
2080
+ {
2081
+ TransNode = T;
2082
+ break;
2083
+ }
2084
+ }
2085
+ }
2086
+
2087
+ if (!TransNode)
2088
+ {
2089
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("No transition from '%s' to '%s'"), *FromState, *ToState));
2090
+ Result->SetBoolField(TEXT("success"), false);
2091
+ return MakeShared<FJsonValueObject>(Result);
2092
+ }
2093
+
2094
+ // Set blend duration
2095
+ double BlendDuration = 0.2;
2096
+ if (Params->TryGetNumberField(TEXT("blendDuration"), BlendDuration))
2097
+ {
2098
+ TransNode->CrossfadeDuration = static_cast<float>(BlendDuration);
2099
+ }
2100
+
2101
+ // Set blend logic (Standard vs Inertialization)
2102
+ FString BlendLogic;
2103
+ if (Params->TryGetStringField(TEXT("blendLogic"), BlendLogic))
2104
+ {
2105
+ if (BlendLogic.Equals(TEXT("Inertialization"), ESearchCase::IgnoreCase))
2106
+ {
2107
+ TransNode->BlendMode = EAlphaBlendOption::Linear;
2108
+ TransNode->LogicType = ETransitionLogicType::TLT_Inertialization;
2109
+ }
2110
+ else // Standard
2111
+ {
2112
+ TransNode->LogicType = ETransitionLogicType::TLT_StandardBlend;
2113
+ }
2114
+ }
2115
+
2116
+ CompileAndSave(AnimBP);
2117
+
2118
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
2119
+ Result->SetStringField(TEXT("fromState"), FromState);
2120
+ Result->SetStringField(TEXT("toState"), ToState);
2121
+ Result->SetNumberField(TEXT("blendDuration"), BlendDuration);
2122
+ Result->SetBoolField(TEXT("success"), true);
2123
+
2124
+ return MakeShared<FJsonValueObject>(Result);
2125
+ }
2126
+
2127
+ TSharedPtr<FJsonValue> FAnimationHandlers::ReadStateMachine(const TSharedPtr<FJsonObject>& Params)
2128
+ {
2129
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
2130
+
2131
+ FString AssetPath;
2132
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
2133
+ {
2134
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'assetPath'"));
2135
+ Result->SetBoolField(TEXT("success"), false);
2136
+ return MakeShared<FJsonValueObject>(Result);
2137
+ }
2138
+
2139
+ FString SMName;
2140
+ if (!Params->TryGetStringField(TEXT("stateMachineName"), SMName))
2141
+ {
2142
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'stateMachineName'"));
2143
+ Result->SetBoolField(TEXT("success"), false);
2144
+ return MakeShared<FJsonValueObject>(Result);
2145
+ }
2146
+
2147
+ UAnimBlueprint* AnimBP = LoadAnimBP(AssetPath);
2148
+ if (!AnimBP)
2149
+ {
2150
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("AnimBlueprint not found: %s"), *AssetPath));
2151
+ Result->SetBoolField(TEXT("success"), false);
2152
+ return MakeShared<FJsonValueObject>(Result);
2153
+ }
2154
+
2155
+ UAnimGraphNode_StateMachine* SMNode = FindStateMachineNode(AnimBP, SMName);
2156
+ if (!SMNode)
2157
+ {
2158
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("State machine '%s' not found"), *SMName));
2159
+ Result->SetBoolField(TEXT("success"), false);
2160
+ return MakeShared<FJsonValueObject>(Result);
2161
+ }
2162
+
2163
+ UAnimationStateMachineGraph* SMGraph = Cast<UAnimationStateMachineGraph>(SMNode->EditorStateMachineGraph);
2164
+ if (!SMGraph)
2165
+ {
2166
+ Result->SetStringField(TEXT("error"), TEXT("State machine has no editor graph"));
2167
+ Result->SetBoolField(TEXT("success"), false);
2168
+ return MakeShared<FJsonValueObject>(Result);
2169
+ }
2170
+
2171
+ // Enumerate states
2172
+ TArray<TSharedPtr<FJsonValue>> StatesArray;
2173
+ for (UEdGraphNode* Node : SMGraph->Nodes)
2174
+ {
2175
+ if (UAnimStateNode* State = Cast<UAnimStateNode>(Node))
2176
+ {
2177
+ TSharedPtr<FJsonObject> StateObj = MakeShared<FJsonObject>();
2178
+ StateObj->SetStringField(TEXT("name"), State->GetStateName());
2179
+
2180
+ // Check for animation asset inside the state
2181
+ if (State->BoundGraph)
2182
+ {
2183
+ for (UEdGraphNode* Inner : State->BoundGraph->Nodes)
2184
+ {
2185
+ if (UAnimGraphNode_AssetPlayerBase* AssetNode = Cast<UAnimGraphNode_AssetPlayerBase>(Inner))
2186
+ {
2187
+ if (UAnimationAsset* Asset = AssetNode->GetAnimationAsset())
2188
+ {
2189
+ StateObj->SetStringField(TEXT("animAsset"), Asset->GetPathName());
2190
+ }
2191
+ }
2192
+ }
2193
+ }
2194
+
2195
+ StatesArray.Add(MakeShared<FJsonValueObject>(StateObj));
2196
+ }
2197
+ }
2198
+ Result->SetArrayField(TEXT("states"), StatesArray);
2199
+
2200
+ // Enumerate transitions
2201
+ TArray<TSharedPtr<FJsonValue>> TransArray;
2202
+ for (UEdGraphNode* Node : SMGraph->Nodes)
2203
+ {
2204
+ if (UAnimStateTransitionNode* T = Cast<UAnimStateTransitionNode>(Node))
2205
+ {
2206
+ TSharedPtr<FJsonObject> TransObj = MakeShared<FJsonObject>();
2207
+
2208
+ UAnimStateNode* Prev = Cast<UAnimStateNode>(T->GetPreviousState());
2209
+ UAnimStateNode* Next = Cast<UAnimStateNode>(T->GetNextState());
2210
+ if (Prev) TransObj->SetStringField(TEXT("fromState"), Prev->GetStateName());
2211
+ if (Next) TransObj->SetStringField(TEXT("toState"), Next->GetStateName());
2212
+
2213
+ TransObj->SetNumberField(TEXT("blendDuration"), T->CrossfadeDuration);
2214
+ TransObj->SetStringField(TEXT("logicType"),
2215
+ T->LogicType == ETransitionLogicType::TLT_Inertialization ? TEXT("Inertialization") : TEXT("Standard"));
2216
+
2217
+ TransArray.Add(MakeShared<FJsonValueObject>(TransObj));
2218
+ }
2219
+ }
2220
+ Result->SetArrayField(TEXT("transitions"), TransArray);
2221
+
2222
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
2223
+ Result->SetStringField(TEXT("stateMachineName"), SMName);
2224
+ Result->SetBoolField(TEXT("success"), true);
2225
+
2226
+ return MakeShared<FJsonValueObject>(Result);
2227
+ }
@@ -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",