AnimGraphTransitionCommandTests.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <MCore/Source/CommandGroup.h>
  9. #include <MCore/Source/ReflectionSerializer.h>
  10. #include <EMotionFX/Source/AnimGraphMotionNode.h>
  11. #include <EMotionFX/Source/AnimGraphStateMachine.h>
  12. #include <EMotionFX/Source/AnimGraphStateTransition.h>
  13. #include <EMotionFX/Source/MotionSet.h>
  14. #include <EMotionFX/CommandSystem/Source/CommandManager.h>
  15. #include <EMotionFX/CommandSystem/Source/AnimGraphConnectionCommands.h>
  16. #include <Tests/AnimGraphFixture.h>
  17. namespace EMotionFX
  18. {
  19. class AnimGraphTransitionCommandsFixture
  20. : public AnimGraphFixture
  21. {
  22. public:
  23. void ConstructGraph() override
  24. {
  25. AnimGraphFixture::ConstructGraph();
  26. m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
  27. m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
  28. /*
  29. +---+ +---+ +---+
  30. | A | | B | | C |
  31. +-+-+ +-+-+ +-+-+
  32. ^ ^ ^
  33. | | |
  34. | +---+---+ |
  35. +----+ Start +----+
  36. +-------+
  37. */
  38. AnimGraphNode* stateStart = aznew AnimGraphMotionNode();
  39. m_rootStateMachine->AddChildNode(stateStart);
  40. stateStart->SetName("Start");
  41. m_rootStateMachine->SetEntryState(stateStart);
  42. AnimGraphNode* stateA = m_motionNodeAnimGraph->GetMotionNodeA();
  43. AnimGraphNode* stateB = m_motionNodeAnimGraph->GetMotionNodeB();
  44. AnimGraphNode* stateC = aznew AnimGraphMotionNode();
  45. stateC->SetName("C");
  46. m_rootStateMachine->AddChildNode(stateC);
  47. m_transitionLeft = AddTransition(stateStart, stateA, 1.0f);
  48. m_transitionLeft->SetCanBeInterrupted(true);
  49. m_transitionMiddle = AddTransition(stateStart, stateB, 1.0f);
  50. m_transitionMiddle->SetCanBeInterrupted(true);
  51. m_transitionMiddle->SetCanInterruptOtherTransitions(true);
  52. m_transitionRight = AddTransition(stateStart, stateC, 1.0f);
  53. m_transitionRight->SetCanInterruptOtherTransitions(true);
  54. m_motionNodeAnimGraph->InitAfterLoading();
  55. }
  56. void SetUp() override
  57. {
  58. AnimGraphFixture::SetUp();
  59. m_animGraphInstance->Destroy();
  60. m_animGraphInstance = m_motionNodeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet);
  61. }
  62. void TearDown() override
  63. {
  64. if (m_animGraphInstance)
  65. {
  66. m_animGraphInstance->Destroy();
  67. m_animGraphInstance = nullptr;
  68. }
  69. m_motionNodeAnimGraph.reset();
  70. AnimGraphFixture::TearDown();
  71. }
  72. AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
  73. AnimGraphStateTransition* m_transitionLeft = nullptr;
  74. AnimGraphStateTransition* m_transitionMiddle = nullptr;
  75. AnimGraphStateTransition* m_transitionRight = nullptr;
  76. };
  77. TEST_F(AnimGraphTransitionCommandsFixture, RemoveTransitionPartOfCanBeInterruptedByTransitionIdsTest)
  78. {
  79. AZStd::string result;
  80. CommandSystem::CommandManager commandManager;
  81. MCore::CommandGroup commandGroup;
  82. const AZStd::vector<AZ::u64> onlyRightId = { m_transitionRight->GetId() };
  83. // 1. Adjust can be interrupted by transition ids for the left transition
  84. AZStd::vector<AZ::u64> canBeInterruptedByTransitionIds;
  85. canBeInterruptedByTransitionIds.emplace_back(m_transitionMiddle->GetId());
  86. canBeInterruptedByTransitionIds.emplace_back(m_transitionRight->GetId());
  87. // Serialize the attribute into a string so we can pass it as a command parameter.
  88. const AZStd::string attributesString = AZStd::string::format("-canBeInterruptedByTransitionIds {%s}",
  89. MCore::ReflectionSerializer::Serialize(&canBeInterruptedByTransitionIds).GetValue().c_str());
  90. // Construct the command and let it adjust the can be interrupted by transition id mask.
  91. CommandSystem::AdjustTransition(m_transitionLeft,
  92. /*isDisabled*/AZStd::nullopt, /*sourceNode*/AZStd::nullopt, /*targetNode*/AZStd::nullopt,
  93. /*startOffsetX*/AZStd::nullopt, /*startOffsetY*/AZStd::nullopt, /*endOffsetX*/AZStd::nullopt, /*endOffsetY*/AZStd::nullopt,
  94. attributesString, /*serializedMembers=*/AZStd::nullopt,
  95. &commandGroup);
  96. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
  97. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds(), canBeInterruptedByTransitionIds)
  98. << "The can be interrupted by transition ids list should contain both, the middle as well as the right transition ids";
  99. // 2. Remove the middle transition that was part of the can be interrupted by transition ids list
  100. // and make sure it got removed from there.
  101. commandGroup.RemoveAllCommands();
  102. {
  103. AZStd::vector<EMotionFX::AnimGraphStateTransition*> transitionList;
  104. CommandSystem::DeleteStateTransition(&commandGroup, m_transitionMiddle, transitionList);
  105. }
  106. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
  107. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds(), onlyRightId)
  108. << "The middle transition should be removed from the can be interrupted by list.";
  109. // 3. Same for the right transition
  110. commandGroup.RemoveAllCommands();
  111. {
  112. AZStd::vector<EMotionFX::AnimGraphStateTransition*> transitionList;
  113. CommandSystem::DeleteStateTransition(&commandGroup, m_transitionRight, transitionList);
  114. }
  115. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
  116. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds().empty(), true)
  117. << "Both transitions should be removed from the can be interrupted by list.";
  118. // 4. Undo remove right transition
  119. EXPECT_TRUE(commandManager.Undo(result));
  120. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds(), onlyRightId)
  121. << "The middle transition should be back in the can be interrupted by list again.";
  122. // 5. Undo remove middle transition
  123. EXPECT_TRUE(commandManager.Undo(result));
  124. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds(), canBeInterruptedByTransitionIds)
  125. << "Both transitions should be back int the can be interrupted by list again.";
  126. // 6. Redo remove middle transition
  127. EXPECT_TRUE(commandManager.Redo(result));
  128. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds(), onlyRightId)
  129. << "The middle transition should be removed from the can be interrupted by list.";
  130. // 7. Redo remove middle transition
  131. EXPECT_TRUE(commandManager.Redo(result));
  132. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds().empty(), true)
  133. << "Both transitions should be removed from the can be interrupted by list.";
  134. }
  135. TEST_F(AnimGraphTransitionCommandsFixture, RecoveringCanBeInterruptedByTransitionIdsAfterRemoveTest)
  136. {
  137. AZStd::string result;
  138. CommandSystem::CommandManager commandManager;
  139. MCore::CommandGroup commandGroup;
  140. // 1. Adjust can be interrupted by transition ids for the left transition
  141. AZStd::vector<AZ::u64> canBeInterruptedByTransitionIds;
  142. canBeInterruptedByTransitionIds.emplace_back(m_transitionMiddle->GetId());
  143. canBeInterruptedByTransitionIds.emplace_back(m_transitionRight->GetId());
  144. // Serialize the attribute into a string so we can pass it as a command parameter.
  145. const AZStd::string attributesString = AZStd::string::format("-canBeInterruptedByTransitionIds {%s}",
  146. MCore::ReflectionSerializer::Serialize(&canBeInterruptedByTransitionIds).GetValue().c_str());
  147. // Construct the command and let it adjust the can be interrupted by transition id mask.
  148. CommandSystem::AdjustTransition(m_transitionLeft,
  149. /*isDisabled*/AZStd::nullopt, /*sourceNode*/AZStd::nullopt, /*targetNode*/AZStd::nullopt,
  150. /*startOffsetX*/AZStd::nullopt, /*startOffsetY*/AZStd::nullopt, /*endOffsetX*/AZStd::nullopt, /*endOffsetY*/AZStd::nullopt,
  151. attributesString, /*serializedMembers=*/AZStd::nullopt,
  152. &commandGroup);
  153. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result))
  154. << "The can be interrupted by transition ids list should contain both, the middle as well as the right transition ids";
  155. // 2. Remove the transition that we just modified
  156. commandGroup.RemoveAllCommands();
  157. {
  158. AZStd::vector<EMotionFX::AnimGraphStateTransition*> transitionList;
  159. CommandSystem::DeleteStateTransition(&commandGroup, m_transitionLeft, transitionList);
  160. }
  161. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
  162. // 3. Undo remove transition
  163. EXPECT_TRUE(commandManager.Undo(result));
  164. // Point m_transitionLeft to the newly created transition after undo.
  165. const AnimGraphConnectionId connectionId = AnimGraphConnectionId::CreateFromString(result);
  166. m_transitionLeft = m_motionNodeAnimGraph->RecursiveFindTransitionById(connectionId);
  167. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds(), canBeInterruptedByTransitionIds)
  168. << "The transition should be back and the can be interrupted by transition ids contain the middle as well as the right transition ids.";
  169. // 4. Undo adjust can be interrupted by transition ids
  170. EXPECT_TRUE(commandManager.Undo(result));
  171. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds().empty(), true)
  172. << "The can be interrupted by list should be back empty.";
  173. // 5. Redo adjust can be interrupted by transition ids
  174. EXPECT_TRUE(commandManager.Redo(result));
  175. EXPECT_EQ(m_transitionLeft->GetCanBeInterruptedByTransitionIds(), canBeInterruptedByTransitionIds)
  176. << "The can be interrupted by transition ids list should contain both, the middle as well as the right transition ids";
  177. // 6. Redo remove middle transition
  178. EXPECT_TRUE(commandManager.Redo(result));
  179. }
  180. TEST_F(AnimGraphTransitionCommandsFixture, CreateWildCardUndo)
  181. {
  182. AZStd::string result;
  183. CommandSystem::CommandManager commandManager;
  184. size_t numTransitions = m_rootStateMachine->GetNumTransitions();
  185. AZStd::string commandString;
  186. commandString = AZStd::string::format("AnimGraphCreateConnection -animGraphID %d -sourceNode \"\" -targetNode \"A\" -sourcePort 0 -targetPort 0 -startOffsetX 0 -startOffsetY 0 -endOffsetX 0 -endOffsetY 0 -transitionType \"%s\"",
  187. m_motionNodeAnimGraph->GetID(),
  188. azrtti_typeid<EMotionFX::AnimGraphStateTransition>().ToString<AZStd::string>().c_str());
  189. ASSERT_TRUE(commandManager.ExecuteCommand(commandString, result, /*addToHistory=*/true))
  190. << "The command execution failed";
  191. ASSERT_EQ(m_rootStateMachine->GetNumTransitions(), numTransitions + 1)
  192. << "The wildcard transition doesn't seem to be added.";
  193. ASSERT_TRUE(commandManager.Undo(result));
  194. ASSERT_EQ(m_rootStateMachine->GetNumTransitions(), numTransitions)
  195. << "Undo of the wild card creation seem to not have removed the transition.";
  196. ASSERT_TRUE(commandManager.Redo(result));
  197. ASSERT_EQ(m_rootStateMachine->GetNumTransitions(), numTransitions + 1)
  198. << "The wildcard transition doesn't seem to be added on Redo.";
  199. }
  200. } // namespace EMotionFX