AnimGraphStateMachineInterruptionTests.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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 <AzCore/Serialization/SerializeContext.h>
  9. #include <AzCore/Serialization/Utils.h>
  10. #include <EMotionFX/Source/EventHandler.h>
  11. #include <EMotionFX/Source/AnimGraphMotionNode.h>
  12. #include <EMotionFX/Source/AnimGraphStateMachine.h>
  13. #include <EMotionFX/Source/MotionSet.h>
  14. #include <Tests/AnimGraphEventHandlerCounter.h>
  15. #include <Tests/AnimGraphFixture.h>
  16. namespace EMotionFX
  17. {
  18. struct AnimGraphStateMachine_InterruptionTestData
  19. {
  20. // Graph construction data.
  21. float m_transitionLeftBlendTime;
  22. float m_transitionLeftCountDownTime;
  23. float m_transitionMiddleBlendTime;
  24. float m_transitionMiddleCountDownTime;
  25. float m_transitionRightBlendTime;
  26. float m_transitionRightCountDownTime;
  27. // Per frame checks.
  28. struct ActiveObjectsAtFrame
  29. {
  30. AZ::u32 m_frameNr;
  31. bool m_stateA;
  32. bool m_stateB;
  33. bool m_stateC;
  34. bool m_transitionLeft;
  35. bool m_transitionMiddle;
  36. bool m_transitionRight;
  37. AZ::u32 m_numStatesEntering;
  38. AZ::u32 m_numStatesEntered;
  39. AZ::u32 m_numStatesExited;
  40. AZ::u32 m_numStatesEnded;
  41. AZ::u32 m_numTransitionsStarted;
  42. AZ::u32 m_numTransitionsEnded;
  43. };
  44. AZStd::fixed_vector<ActiveObjectsAtFrame, 16> m_activeObjectsAtFrame;
  45. };
  46. class AnimGraphStateMachine_InterruptionFixture
  47. : public AnimGraphFixture
  48. , public ::testing::WithParamInterface<AnimGraphStateMachine_InterruptionTestData>
  49. {
  50. public:
  51. void ConstructGraph() override
  52. {
  53. const AnimGraphStateMachine_InterruptionTestData param = GetParam();
  54. AnimGraphFixture::ConstructGraph();
  55. /*
  56. +---+ +---+ +---+
  57. | A | | B | | C |
  58. +-+-+ +-+-+ +-+-+
  59. ^ ^ ^
  60. | | |
  61. | +---+---+ |
  62. +----+ Start +----+
  63. +-------+
  64. */
  65. m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
  66. m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
  67. AnimGraphNode* stateStart = aznew AnimGraphMotionNode();
  68. m_rootStateMachine->AddChildNode(stateStart);
  69. m_rootStateMachine->SetEntryState(stateStart);
  70. AnimGraphNode* stateA = m_motionNodeAnimGraph->GetMotionNodeA();
  71. AnimGraphNode* stateB = m_motionNodeAnimGraph->GetMotionNodeB();
  72. AnimGraphNode* stateC = aznew AnimGraphMotionNode();
  73. stateC->SetName("C");
  74. m_rootStateMachine->AddChildNode(stateC);
  75. AnimGraphStateTransition* transitionLeft = AddTransitionWithTimeCondition(stateStart,
  76. stateA,
  77. param.m_transitionLeftBlendTime/*blendTime*/,
  78. param.m_transitionLeftCountDownTime)/*countDownTime*/;
  79. transitionLeft->SetCanBeInterrupted(true);
  80. AnimGraphStateTransition* transitionMiddle = AddTransitionWithTimeCondition(stateStart,
  81. stateB,
  82. param.m_transitionMiddleBlendTime,
  83. param.m_transitionMiddleCountDownTime);
  84. transitionMiddle->SetCanBeInterrupted(true);
  85. transitionMiddle->SetCanInterruptOtherTransitions(true);
  86. AnimGraphStateTransition* transitionRight = AddTransitionWithTimeCondition(stateStart,
  87. stateC,
  88. param.m_transitionRightBlendTime,
  89. param.m_transitionRightCountDownTime);
  90. transitionRight->SetCanInterruptOtherTransitions(true);
  91. m_motionNodeAnimGraph->InitAfterLoading();
  92. }
  93. void SetUp() override
  94. {
  95. AnimGraphFixture::SetUp();
  96. m_animGraphInstance->Destroy();
  97. m_animGraphInstance = m_motionNodeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet);
  98. m_eventHandler = aznew AnimGraphEventHandlerCounter();
  99. m_animGraphInstance->AddEventHandler(m_eventHandler);
  100. }
  101. void TearDown() override
  102. {
  103. m_animGraphInstance->RemoveEventHandler(m_eventHandler);
  104. delete m_eventHandler;
  105. if (m_animGraphInstance)
  106. {
  107. m_animGraphInstance->Destroy();
  108. m_animGraphInstance = nullptr;
  109. }
  110. m_motionNodeAnimGraph.reset();
  111. AnimGraphFixture::TearDown();
  112. }
  113. AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
  114. AnimGraphEventHandlerCounter* m_eventHandler = nullptr;
  115. };
  116. TEST_P(AnimGraphStateMachine_InterruptionFixture, AnimGraphStateMachine_InterruptionTest)
  117. {
  118. // Defer enter entry state on state machine update.
  119. m_eventHandler->m_numStatesEntering -= 1;
  120. m_eventHandler->m_numStatesEntered -= 1;
  121. m_eventHandler->m_numStatesExited -= 1;
  122. m_eventHandler->m_numStatesEnded -= 1;
  123. Simulate(20.0f/*simulationTime*/, 60.0f/*expectedFps*/, 0.0f/*fpsVariance*/,
  124. /*preCallback*/[]([[maybe_unused]] AnimGraphInstance* animGraphInstance){},
  125. /*postCallback*/[]([[maybe_unused]] AnimGraphInstance* animGraphInstance){},
  126. /*preUpdateCallback*/[](AnimGraphInstance*, float, float, int) {},
  127. /*postUpdateCallback*/[this](AnimGraphInstance* animGraphInstance, [[maybe_unused]] float time, [[maybe_unused]] float timeDelta, int frame)
  128. {
  129. const auto& activeObjectsAtFrame = GetParam().m_activeObjectsAtFrame;
  130. const AnimGraphStateMachine* stateMachine = this->m_rootStateMachine;
  131. const AZStd::vector<AnimGraphNode*>& activeStates = stateMachine->GetActiveStates(animGraphInstance);
  132. const AZStd::vector<AnimGraphStateTransition*>& activeTransitions = stateMachine->GetActiveTransitions(animGraphInstance);
  133. AnimGraphStateMachine_InterruptionTestData::ActiveObjectsAtFrame compareAgainst;
  134. compareAgainst.m_stateA = AZStd::find_if(activeStates.begin(), activeStates.end(),
  135. [](AnimGraphNode* element) -> bool { return element->GetNameString() == "A"; }) != activeStates.end();
  136. compareAgainst.m_stateB = AZStd::find_if(activeStates.begin(), activeStates.end(),
  137. [](AnimGraphNode* element) -> bool { return element->GetNameString() == "B"; }) != activeStates.end();
  138. compareAgainst.m_stateC = AZStd::find_if(activeStates.begin(), activeStates.end(),
  139. [](AnimGraphNode* element) -> bool { return element->GetNameString() == "C"; }) != activeStates.end();
  140. compareAgainst.m_transitionLeft = AZStd::find_if(activeTransitions.begin(), activeTransitions.end(),
  141. [](AnimGraphStateTransition* element) -> bool { return element->GetTargetNode()->GetNameString() == "A"; }) != activeTransitions.end();
  142. compareAgainst.m_transitionMiddle = AZStd::find_if(activeTransitions.begin(), activeTransitions.end(),
  143. [](AnimGraphStateTransition* element) -> bool { return element->GetTargetNode()->GetNameString() == "B"; }) != activeTransitions.end();
  144. compareAgainst.m_transitionRight = AZStd::find_if(activeTransitions.begin(), activeTransitions.end(),
  145. [](AnimGraphStateTransition* element) -> bool { return element->GetTargetNode()->GetNameString() == "C"; }) != activeTransitions.end();
  146. for (const AnimGraphStateMachine_InterruptionTestData::ActiveObjectsAtFrame& activeObjects : activeObjectsAtFrame)
  147. {
  148. if (activeObjects.m_frameNr == static_cast<AZ::u32>(frame))
  149. {
  150. // Check which states and transitions are active and compare it to the expected ones.
  151. EXPECT_THAT(activeObjects.m_stateA, ::testing::Eq(compareAgainst.m_stateA))
  152. << "State A expected to be " << (activeObjects.m_stateA ? "active." : "inactive.");
  153. EXPECT_THAT(activeObjects.m_stateB, ::testing::Eq(compareAgainst.m_stateB))
  154. << "State B expected to be " << (activeObjects.m_stateB ? "active." : "inactive.");
  155. EXPECT_THAT(activeObjects.m_stateC, ::testing::Eq(compareAgainst.m_stateC))
  156. << "State C expected to be " << (activeObjects.m_stateB ? "active." : "inactive.");
  157. EXPECT_THAT(activeObjects.m_transitionLeft, ::testing::Eq(compareAgainst.m_transitionLeft))
  158. << "Transition Start->A expected to be " << (activeObjects.m_transitionLeft ? "active." : "inactive.");
  159. EXPECT_THAT(activeObjects.m_transitionMiddle, ::testing::Eq(compareAgainst.m_transitionMiddle))
  160. << "Transition Start->B expected to be " << (activeObjects.m_transitionMiddle ? "active." : "inactive.");
  161. EXPECT_THAT(activeObjects.m_transitionRight, ::testing::Eq(compareAgainst.m_transitionRight))
  162. << "Transition Start->C expected to be " << (activeObjects.m_transitionRight ? "active." : "inactive.");
  163. // Check anim graph events.
  164. EXPECT_EQ(this->m_eventHandler->m_numStatesEntering, activeObjects.m_numStatesEntering)
  165. << this->m_eventHandler->m_numStatesEntering << " states entering while " << activeObjects.m_numStatesEntering << " are expected.";
  166. EXPECT_EQ(this->m_eventHandler->m_numStatesEntered, activeObjects.m_numStatesEntered)
  167. << this->m_eventHandler->m_numStatesEntered << " states entered while " << activeObjects.m_numStatesEntered << " are expected.";
  168. EXPECT_EQ(this->m_eventHandler->m_numStatesExited, activeObjects.m_numStatesExited)
  169. << this->m_eventHandler->m_numStatesExited << " states exited while " << activeObjects.m_numStatesExited << " are expected.";
  170. EXPECT_EQ(this->m_eventHandler->m_numStatesEnded, activeObjects.m_numStatesEnded)
  171. << this->m_eventHandler->m_numStatesEnded << " states ended while " << activeObjects.m_numStatesEnded << " are expected.";
  172. EXPECT_EQ(this->m_eventHandler->m_numTransitionsStarted, activeObjects.m_numTransitionsStarted)
  173. << this->m_eventHandler->m_numTransitionsStarted << " transitions started while " << activeObjects.m_numTransitionsStarted << " are expected.";
  174. EXPECT_EQ(this->m_eventHandler->m_numTransitionsEnded, activeObjects.m_numTransitionsEnded)
  175. << this->m_eventHandler->m_numTransitionsEnded << " transitions ended while " << activeObjects.m_numTransitionsEnded << " are expected.";
  176. }
  177. }
  178. }
  179. );
  180. }
  181. AZStd::array animGraphStateMachineInterruptionTestData
  182. {
  183. // Start transition Start->A, interrupt with Start->B while Start->A is still transitioning
  184. // Interrupt with Start->C while the others keep transitioning till Start->C is done
  185. AnimGraphStateMachine_InterruptionTestData{
  186. 10.0f/*transitionLeftBlendTime*/,
  187. 1.0f/*transitionLeftCountDownTime*/,
  188. 10.0f/*transitionMiddleBlendTime*/,
  189. 2.0f/*transitionMiddleCountDownTime*/,
  190. 5.0/*transitionRightBlendTime*/,
  191. 3.0f/*transitionRightCountDownTime*/,
  192. {
  193. {
  194. 0/*_frameNr*/,
  195. false/*stateA*/, false/*stateB*/, false/*stateC*/,
  196. false/*transitionLeft*/, false/*transitionMiddle*/, false/*transitionRight*/,
  197. 0/*numStatesEntering*/, 0/*numStatesEntered*/,
  198. 0/*numStatesExited*/, 0/*numStatesEnded*/,
  199. 0/*numTransitionsStarted*/, 0/*numTransitionsEnded*/
  200. },
  201. {
  202. 60, // Start transition: Start->A
  203. true, false, false,
  204. true, false, false,
  205. 1, 0,
  206. 1, 0,
  207. 1, 0
  208. },
  209. {
  210. 90,
  211. true, false, false,
  212. true, false, false,
  213. 1, 0,
  214. 1, 0,
  215. 1, 0
  216. },
  217. {
  218. 120, // Interrupt transition: Start->A and start transition Start->B
  219. true, true, false,
  220. true, true, false,
  221. 2, 0,
  222. 2, 0,
  223. 2, 0
  224. },
  225. {
  226. 150,
  227. true, true, false,
  228. true, true, false,
  229. 2, 0,
  230. 2, 0,
  231. 2, 0
  232. },
  233. {
  234. 300, // Interrupt transition: Start->B and start transition Start->C
  235. true, true, true,
  236. true, true, true,
  237. 3, 0,
  238. 3, 0,
  239. 3, 0
  240. },
  241. {
  242. 330,
  243. true, true, true,
  244. true, true, true,
  245. 3, 0,
  246. 3, 0,
  247. 3, 0
  248. },
  249. {
  250. 480,
  251. false, false, true,
  252. false, false, false,
  253. 3, 3,
  254. 3, 3,
  255. 3, 3
  256. }
  257. }
  258. },
  259. // Start transition Start->A and let Start->B/C interrupt it
  260. // Start->A/B finishes and holds the target state active while Start->C is finishing
  261. AnimGraphStateMachine_InterruptionTestData{
  262. 2.0f/*transitionLeftBlendTime*/,
  263. 1.0f/*transitionLeftCountDownTime*/,
  264. 3.0f/*transitionMiddleBlendTime*/,
  265. 2.0f/*transitionMiddleCountDownTime*/,
  266. 10.0/*transitionRightBlendTime*/,
  267. 4.0f/*transitionRightCountDownTime*/,
  268. {
  269. {
  270. 0,
  271. false, false, false,
  272. false, false, false,
  273. 0, 0,
  274. 0, 0,
  275. 0, 0
  276. },
  277. {
  278. 60, // Start transition: Start->A
  279. true, false, false,
  280. true, false, false,
  281. 1, 0,
  282. 1, 0,
  283. 1, 0
  284. },
  285. {
  286. 90,
  287. true, false, false,
  288. true, false, false,
  289. 1, 0,
  290. 1, 0,
  291. 1, 0
  292. },
  293. {
  294. 120, // Interrupt transition: Start->A and start transition Start->B
  295. true, true, false,
  296. true, true, false,
  297. 2, 0,
  298. 2, 0,
  299. 2, 0
  300. },
  301. {
  302. 150,
  303. true, true, false,
  304. true, true, false,
  305. 2, 0,
  306. 2, 0,
  307. 2, 0
  308. },
  309. {
  310. 180, // Start->A finishes and stays on the transition stack to keep the target state active
  311. true, true, false,
  312. true, true, false,
  313. 2, 0,
  314. 2, 0,
  315. 2, 0
  316. },
  317. {
  318. 240, // Interrupt transition Start->B with Start->C
  319. true, true, true,
  320. true, true, true,
  321. 3, 0,
  322. 3, 0,
  323. 3, 0
  324. },
  325. {
  326. 300, // Transition Start->B finishes and stays on the transition stack to keep the target state active
  327. true, true, true,
  328. true, true, true,
  329. 3, 0,
  330. 3, 0,
  331. 3, 0
  332. },
  333. {
  334. 330,
  335. true, true, true,
  336. true, true, true,
  337. 3, 0,
  338. 3, 0,
  339. 3, 0
  340. },
  341. {
  342. 840, // Latest active transition finishes and cleared the transition stack
  343. false, false, true,
  344. false, false, false,
  345. 3, 3,
  346. 3, 3,
  347. 3, 3
  348. }
  349. }
  350. }
  351. };
  352. INSTANTIATE_TEST_CASE_P(AnimGraphStateMachine_InterruptionTest,
  353. AnimGraphStateMachine_InterruptionFixture,
  354. ::testing::ValuesIn(animGraphStateMachineInterruptionTestData)
  355. );
  356. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  357. struct AnimGraphStateMachine_InterruptionPropertiesTestData
  358. {
  359. float m_transitionLeftBlendTime;
  360. float m_transitionLeftCountDownTime;
  361. float m_transitionRightBlendTime;
  362. float m_transitionRightCountDownTime;
  363. AnimGraphStateTransition::EInterruptionMode m_interruptionMode;
  364. float m_maxBlendWeight;
  365. AnimGraphStateTransition::EInterruptionBlendBehavior m_interruptionBlendBehavior;
  366. };
  367. class AnimGraphStateMachine_InterruptionPropertiesFixture
  368. : public AnimGraphFixture
  369. , public ::testing::WithParamInterface<AnimGraphStateMachine_InterruptionPropertiesTestData>
  370. {
  371. public:
  372. void ConstructGraph() override
  373. {
  374. const AnimGraphStateMachine_InterruptionPropertiesTestData param = GetParam();
  375. AnimGraphFixture::ConstructGraph();
  376. /*
  377. +---+ +---+
  378. | A | | B |
  379. +-+-+ +-+-+
  380. ^ ^
  381. | +---+---+ |
  382. +----+ Start +----+
  383. +-------+
  384. */
  385. m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
  386. m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
  387. AnimGraphNode* stateStart = aznew AnimGraphMotionNode();
  388. m_rootStateMachine->AddChildNode(stateStart);
  389. m_rootStateMachine->SetEntryState(stateStart);
  390. AnimGraphNode* stateA = m_motionNodeAnimGraph->GetMotionNodeA();
  391. AnimGraphNode* stateB = m_motionNodeAnimGraph->GetMotionNodeB();
  392. // Start->A (can be interrupted)
  393. m_transitionLeft = AddTransitionWithTimeCondition(stateStart,
  394. stateA,
  395. param.m_transitionLeftBlendTime,
  396. param.m_transitionLeftCountDownTime);
  397. m_transitionLeft->SetCanBeInterrupted(true);
  398. m_transitionLeft->SetInterruptionMode(param.m_interruptionMode);
  399. m_transitionLeft->SetMaxInterruptionBlendWeight(param.m_maxBlendWeight);
  400. m_transitionLeft->SetInterruptionBlendBehavior(param.m_interruptionBlendBehavior);
  401. // Start->B (interrupting transition)
  402. m_transitionRight = AddTransitionWithTimeCondition(stateStart,
  403. stateB,
  404. param.m_transitionRightBlendTime,
  405. param.m_transitionRightCountDownTime);
  406. m_transitionRight->SetCanInterruptOtherTransitions(true);
  407. m_motionNodeAnimGraph->InitAfterLoading();
  408. }
  409. void SetUp() override
  410. {
  411. AnimGraphFixture::SetUp();
  412. m_animGraphInstance->Destroy();
  413. m_animGraphInstance = m_motionNodeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet);
  414. }
  415. void TearDown() override
  416. {
  417. if (m_animGraphInstance)
  418. {
  419. m_animGraphInstance->Destroy();
  420. m_animGraphInstance = nullptr;
  421. }
  422. m_motionNodeAnimGraph.reset();
  423. AnimGraphFixture::TearDown();
  424. }
  425. AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
  426. AnimGraphStateTransition* m_transitionLeft = nullptr;
  427. AnimGraphStateTransition* m_transitionRight = nullptr;
  428. };
  429. TEST_P(AnimGraphStateMachine_InterruptionPropertiesFixture, AnimGraphStateMachine_InterruptionPropertiesTest)
  430. {
  431. bool prevGotInterrupted = false;
  432. float prevBlendWeight = 0.0f;
  433. Simulate(2.0f /*simulationTime*/, 10.0f /*expectedFps*/, 0.0f /*fpsVariance*/,
  434. /*preCallback*/[]([[maybe_unused]] AnimGraphInstance* animGraphInstance) {},
  435. /*postCallback*/[]([[maybe_unused]] AnimGraphInstance* animGraphInstance) {},
  436. /*preUpdateCallback*/[](AnimGraphInstance*, float, float, int) {},
  437. /*postUpdateCallback*/[this, &prevGotInterrupted, &prevBlendWeight](AnimGraphInstance* animGraphInstance, [[maybe_unused]] float time, [[maybe_unused]] float timeDelta, [[maybe_unused]] int frame)
  438. {
  439. const float maxInterruptionBlendWeight = m_transitionLeft->GetMaxInterruptionBlendWeight();
  440. const bool gotInterrupted = m_transitionLeft->GotInterrupted(animGraphInstance);
  441. const bool gotInterruptedThisFrame = gotInterrupted && !prevGotInterrupted;
  442. const float blendWeight = m_transitionLeft->GetBlendWeight(animGraphInstance);
  443. if (m_transitionLeft->GetInterruptionMode() == AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight)
  444. {
  445. if (blendWeight > maxInterruptionBlendWeight)
  446. {
  447. EXPECT_FALSE(gotInterruptedThisFrame) << "No interruption should be possible anymore at blend weight " << blendWeight << ".";
  448. }
  449. else
  450. {
  451. if (m_transitionRight->CheckIfIsReady(animGraphInstance))
  452. {
  453. EXPECT_TRUE(gotInterrupted) << "Interruption should have happened.";
  454. }
  455. }
  456. }
  457. if (gotInterrupted && !gotInterruptedThisFrame &&
  458. m_transitionLeft->GetInterruptionBlendBehavior() == AnimGraphStateTransition::EInterruptionBlendBehavior::Stop)
  459. {
  460. EXPECT_FLOAT_EQ(prevBlendWeight, blendWeight) << "The blend weight should not change anymore after the transition got interrupted when using blend behavior stop.";
  461. }
  462. prevGotInterrupted = gotInterrupted;
  463. prevBlendWeight = blendWeight;
  464. }
  465. );
  466. }
  467. AZStd::array animGraphStateMachineInterruptionPropertiesTestData
  468. {
  469. // Enable right transition at 0.5 while this is over the max blend weight already, don't allow interruption.
  470. AnimGraphStateMachine_InterruptionPropertiesTestData{
  471. 1.0f /*transitionLeftBlendTime*/,
  472. 0.0f /*transitionLeftCountDownTime*/,
  473. 1.0f /*transitionRightBlendTime*/,
  474. 0.5f /*transitionRightCountDownTime*/,
  475. AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight /*interruptionMode*/,
  476. 0.1f /*maxBlendWeight*/,
  477. AnimGraphStateTransition::EInterruptionBlendBehavior::Continue /*interruptionBlendBehavior*/
  478. },
  479. // Right transition ready after 0.5 while still in range for the max blend weight, interruption expected.
  480. AnimGraphStateMachine_InterruptionPropertiesTestData{
  481. 1.0f,
  482. 0.0f,
  483. 1.0f,
  484. 0.5f,
  485. AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight,
  486. 0.6f,
  487. AnimGraphStateTransition::EInterruptionBlendBehavior::Continue
  488. },
  489. // Interruption always allowed.
  490. AnimGraphStateMachine_InterruptionPropertiesTestData{
  491. 0.5f,
  492. 0.0f,
  493. 0.5f,
  494. 0.2f,
  495. AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight,
  496. 1.0f,
  497. AnimGraphStateTransition::EInterruptionBlendBehavior::Continue
  498. },
  499. // Test if interrupted transitions stop transitioning with blend behavior set to stop.
  500. AnimGraphStateMachine_InterruptionPropertiesTestData{
  501. 1.0f,
  502. 0.0f,
  503. 1.0f,
  504. 0.5f,
  505. AnimGraphStateTransition::EInterruptionMode::AlwaysAllowed,
  506. 0.0f,
  507. AnimGraphStateTransition::EInterruptionBlendBehavior::Stop
  508. },
  509. };
  510. INSTANTIATE_TEST_CASE_P(AnimGraphStateMachine_InterruptionPropertiesTest,
  511. AnimGraphStateMachine_InterruptionPropertiesFixture,
  512. ::testing::ValuesIn(animGraphStateMachineInterruptionPropertiesTestData)
  513. );
  514. } // EMotionFX