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.
- package/dist/tools/animation.js +23 -1
- package/dist/tools/animation.js.map +1 -1
- package/package.json +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +659 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +8 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +1 -0
package/dist/tools/animation.js
CHANGED
|
@@ -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":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,EAAE,EAAgB,MAAM,aAAa,CAAC;AAE7D,MAAM,CAAC,MAAM,aAAa,GAAY,YAAY,CAChD,WAAW,EACX,sFAAsF,EACtF;IACE,mBAAmB,EAAG,EAAE,CAAC,qBAAqB,CAAC;IAC/C,YAAY,EAAU,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IAClF,aAAa,EAAS,EAAE,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACnF,eAAe,EAAO,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IAChF,IAAI,EAAkB,EAAE,CAAC,kBAAkB,CAAC;IAC5C,cAAc,EAAQ,EAAE,CAAC,qBAAqB,CAAC;IAC/C,qBAAqB,EAAE,EAAE,CAAC,uBAAuB,CAAC;IAClD,iBAAiB,EAAK,EAAE,CAAC,mBAAmB,CAAC;IAC7C,UAAU,EAAY,EAAE,CAAC,iBAAiB,CAAC;IAC3C,iBAAiB,EAAK,EAAE,CAAC,mBAAmB,CAAC;IAC7C,YAAY,EAAU,EAAE,CAAC,cAAc,CAAC;IACxC,oBAAoB,EAAE,EAAE,CAAC,sBAAsB,CAAC;IAChD,iBAAiB,EAAK,EAAE,CAAC,wBAAwB,CAAC;IAClD,eAAe,EAAO,EAAE,CAAC,iBAAiB,CAAC;IAC3C,kBAAkB,EAAI,EAAE,CAAC,oBAAoB,CAAC;IAC9C,mBAAmB,EAAG,EAAE,CAAC,qBAAqB,CAAC;IAC/C,oBAAoB,EAAE,EAAE,CAAC,sBAAsB,CAAC;IAChD,sBAAsB,EAAE,EAAE,CAAC,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"animation.js","sourceRoot":"","sources":["../../src/tools/animation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -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
|
};
|