You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
549 lines
24 KiB
C++
549 lines
24 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/Serialization/Utils.h>
|
|
#include <EMotionFX/Source/EventHandler.h>
|
|
#include <EMotionFX/Source/AnimGraphMotionNode.h>
|
|
#include <EMotionFX/Source/AnimGraphStateMachine.h>
|
|
#include <EMotionFX/Source/MotionSet.h>
|
|
#include <Tests/AnimGraphEventHandlerCounter.h>
|
|
#include <Tests/AnimGraphFixture.h>
|
|
|
|
|
|
namespace EMotionFX
|
|
{
|
|
struct AnimGraphStateMachine_InterruptionTestData
|
|
{
|
|
// Graph construction data.
|
|
float m_transitionLeftBlendTime;
|
|
float m_transitionLeftCountDownTime;
|
|
float m_transitionMiddleBlendTime;
|
|
float m_transitionMiddleCountDownTime;
|
|
float m_transitionRightBlendTime;
|
|
float m_transitionRightCountDownTime;
|
|
|
|
// Per frame checks.
|
|
struct ActiveObjectsAtFrame
|
|
{
|
|
AZ::u32 m_frameNr;
|
|
|
|
bool m_stateA;
|
|
bool m_stateB;
|
|
bool m_stateC;
|
|
bool m_transitionLeft;
|
|
bool m_transitionMiddle;
|
|
bool m_transitionRight;
|
|
|
|
AZ::u32 m_numStatesEntering;
|
|
AZ::u32 m_numStatesEntered;
|
|
AZ::u32 m_numStatesExited;
|
|
AZ::u32 m_numStatesEnded;
|
|
AZ::u32 m_numTransitionsStarted;
|
|
AZ::u32 m_numTransitionsEnded;
|
|
};
|
|
|
|
std::vector<ActiveObjectsAtFrame> m_activeObjectsAtFrame;
|
|
};
|
|
|
|
class AnimGraphStateMachine_InterruptionFixture
|
|
: public AnimGraphFixture
|
|
, public ::testing::WithParamInterface<AnimGraphStateMachine_InterruptionTestData>
|
|
{
|
|
public:
|
|
void ConstructGraph() override
|
|
{
|
|
const AnimGraphStateMachine_InterruptionTestData param = GetParam();
|
|
AnimGraphFixture::ConstructGraph();
|
|
|
|
/*
|
|
+---+ +---+ +---+
|
|
| A | | B | | C |
|
|
+-+-+ +-+-+ +-+-+
|
|
^ ^ ^
|
|
| | |
|
|
| +---+---+ |
|
|
+----+ Start +----+
|
|
+-------+
|
|
*/
|
|
m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
|
|
m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
|
|
|
|
AnimGraphNode* stateStart = aznew AnimGraphMotionNode();
|
|
m_rootStateMachine->AddChildNode(stateStart);
|
|
m_rootStateMachine->SetEntryState(stateStart);
|
|
|
|
AnimGraphNode* stateA = m_motionNodeAnimGraph->GetMotionNodeA();
|
|
AnimGraphNode* stateB = m_motionNodeAnimGraph->GetMotionNodeB();
|
|
AnimGraphNode* stateC = aznew AnimGraphMotionNode();
|
|
stateC->SetName("C");
|
|
m_rootStateMachine->AddChildNode(stateC);
|
|
|
|
AnimGraphStateTransition* transitionLeft = AddTransitionWithTimeCondition(stateStart,
|
|
stateA,
|
|
param.m_transitionLeftBlendTime/*blendTime*/,
|
|
param.m_transitionLeftCountDownTime)/*countDownTime*/;
|
|
transitionLeft->SetCanBeInterrupted(true);
|
|
|
|
AnimGraphStateTransition* transitionMiddle = AddTransitionWithTimeCondition(stateStart,
|
|
stateB,
|
|
param.m_transitionMiddleBlendTime,
|
|
param.m_transitionMiddleCountDownTime);
|
|
transitionMiddle->SetCanBeInterrupted(true);
|
|
transitionMiddle->SetCanInterruptOtherTransitions(true);
|
|
|
|
AnimGraphStateTransition* transitionRight = AddTransitionWithTimeCondition(stateStart,
|
|
stateC,
|
|
param.m_transitionRightBlendTime,
|
|
param.m_transitionRightCountDownTime);
|
|
transitionRight->SetCanInterruptOtherTransitions(true);
|
|
|
|
m_motionNodeAnimGraph->InitAfterLoading();
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
AnimGraphFixture::SetUp();
|
|
m_animGraphInstance->Destroy();
|
|
m_animGraphInstance = m_motionNodeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet);
|
|
|
|
m_eventHandler = aznew AnimGraphEventHandlerCounter();
|
|
m_animGraphInstance->AddEventHandler(m_eventHandler);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_animGraphInstance->RemoveEventHandler(m_eventHandler);
|
|
delete m_eventHandler;
|
|
|
|
AnimGraphFixture::TearDown();
|
|
}
|
|
|
|
AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
|
|
AnimGraphEventHandlerCounter* m_eventHandler = nullptr;
|
|
};
|
|
|
|
TEST_P(AnimGraphStateMachine_InterruptionFixture, AnimGraphStateMachine_InterruptionTest)
|
|
{
|
|
// Defer enter entry state on state machine update.
|
|
m_eventHandler->m_numStatesEntering -= 1;
|
|
m_eventHandler->m_numStatesEntered -= 1;
|
|
m_eventHandler->m_numStatesExited -= 1;
|
|
m_eventHandler->m_numStatesEnded -= 1;
|
|
|
|
Simulate(20.0f/*simulationTime*/, 60.0f/*expectedFps*/, 0.0f/*fpsVariance*/,
|
|
/*preCallback*/[this]([[maybe_unused]] AnimGraphInstance* animGraphInstance){},
|
|
/*postCallback*/[this]([[maybe_unused]] AnimGraphInstance* animGraphInstance){},
|
|
/*preUpdateCallback*/[this](AnimGraphInstance*, float, float, int) {},
|
|
/*postUpdateCallback*/[this](AnimGraphInstance* animGraphInstance, [[maybe_unused]] float time, [[maybe_unused]] float timeDelta, int frame)
|
|
{
|
|
const std::vector<AnimGraphStateMachine_InterruptionTestData::ActiveObjectsAtFrame>& activeObjectsAtFrame = GetParam().m_activeObjectsAtFrame;
|
|
const AnimGraphStateMachine* stateMachine = this->m_rootStateMachine;
|
|
const AZStd::vector<AnimGraphNode*>& activeStates = stateMachine->GetActiveStates(animGraphInstance);
|
|
const AZStd::vector<AnimGraphStateTransition*>& activeTransitions = stateMachine->GetActiveTransitions(animGraphInstance);
|
|
|
|
AnimGraphStateMachine_InterruptionTestData::ActiveObjectsAtFrame compareAgainst;
|
|
|
|
compareAgainst.m_stateA = AZStd::find_if(activeStates.begin(), activeStates.end(),
|
|
[](AnimGraphNode* element) -> bool { return element->GetNameString() == "A"; }) != activeStates.end();
|
|
compareAgainst.m_stateB = AZStd::find_if(activeStates.begin(), activeStates.end(),
|
|
[](AnimGraphNode* element) -> bool { return element->GetNameString() == "B"; }) != activeStates.end();
|
|
compareAgainst.m_stateC = AZStd::find_if(activeStates.begin(), activeStates.end(),
|
|
[](AnimGraphNode* element) -> bool { return element->GetNameString() == "C"; }) != activeStates.end();
|
|
|
|
compareAgainst.m_transitionLeft = AZStd::find_if(activeTransitions.begin(), activeTransitions.end(),
|
|
[](AnimGraphStateTransition* element) -> bool { return element->GetTargetNode()->GetNameString() == "A"; }) != activeTransitions.end();
|
|
compareAgainst.m_transitionMiddle = AZStd::find_if(activeTransitions.begin(), activeTransitions.end(),
|
|
[](AnimGraphStateTransition* element) -> bool { return element->GetTargetNode()->GetNameString() == "B"; }) != activeTransitions.end();
|
|
compareAgainst.m_transitionRight = AZStd::find_if(activeTransitions.begin(), activeTransitions.end(),
|
|
[](AnimGraphStateTransition* element) -> bool { return element->GetTargetNode()->GetNameString() == "C"; }) != activeTransitions.end();
|
|
|
|
for (const auto& activeObjects : activeObjectsAtFrame)
|
|
{
|
|
if (activeObjects.m_frameNr == static_cast<AZ::u32>(frame))
|
|
{
|
|
// Check which states and transitions are active and compare it to the expected ones.
|
|
EXPECT_EQ(activeObjects.m_stateA, compareAgainst.m_stateA)
|
|
<< "State A expected to be " << (activeObjects.m_stateA ? "active." : "inactive.");
|
|
EXPECT_EQ(activeObjects.m_stateB, compareAgainst.m_stateB)
|
|
<< "State B expected to be " << (activeObjects.m_stateB ? "active." : "inactive.");
|
|
EXPECT_EQ(activeObjects.m_stateC, compareAgainst.m_stateC)
|
|
<< "State C expected to be " << (activeObjects.m_stateB ? "active." : "inactive.");
|
|
EXPECT_EQ(activeObjects.m_transitionLeft, compareAgainst.m_transitionLeft)
|
|
<< "Transition Start->A expected to be " << (activeObjects.m_transitionLeft ? "active." : "inactive.");
|
|
EXPECT_EQ(activeObjects.m_transitionMiddle, compareAgainst.m_transitionMiddle)
|
|
<< "Transition Start->B expected to be " << (activeObjects.m_transitionMiddle ? "active." : "inactive.");
|
|
EXPECT_EQ(activeObjects.m_transitionRight, compareAgainst.m_transitionRight)
|
|
<< "Transition Start->C expected to be " << (activeObjects.m_transitionRight ? "active." : "inactive.");
|
|
|
|
// Check anim graph events.
|
|
EXPECT_EQ(this->m_eventHandler->m_numStatesEntering, activeObjects.m_numStatesEntering)
|
|
<< this->m_eventHandler->m_numStatesEntering << " states entering while " << activeObjects.m_numStatesEntering << " are expected.";
|
|
EXPECT_EQ(this->m_eventHandler->m_numStatesEntered, activeObjects.m_numStatesEntered)
|
|
<< this->m_eventHandler->m_numStatesEntered << " states entered while " << activeObjects.m_numStatesEntered << " are expected.";
|
|
EXPECT_EQ(this->m_eventHandler->m_numStatesExited, activeObjects.m_numStatesExited)
|
|
<< this->m_eventHandler->m_numStatesExited << " states exited while " << activeObjects.m_numStatesExited << " are expected.";
|
|
EXPECT_EQ(this->m_eventHandler->m_numStatesEnded, activeObjects.m_numStatesEnded)
|
|
<< this->m_eventHandler->m_numStatesEnded << " states ended while " << activeObjects.m_numStatesEnded << " are expected.";
|
|
EXPECT_EQ(this->m_eventHandler->m_numTransitionsStarted, activeObjects.m_numTransitionsStarted)
|
|
<< this->m_eventHandler->m_numTransitionsStarted << " transitions started while " << activeObjects.m_numTransitionsStarted << " are expected.";
|
|
EXPECT_EQ(this->m_eventHandler->m_numTransitionsEnded, activeObjects.m_numTransitionsEnded)
|
|
<< this->m_eventHandler->m_numTransitionsEnded << " transitions ended while " << activeObjects.m_numTransitionsEnded << " are expected.";
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
std::vector<AnimGraphStateMachine_InterruptionTestData> animGraphStateMachineInterruptionTestData
|
|
{
|
|
// Start transition Start->A, interrupt with Start->B while Start->A is still transitioning
|
|
// Interrupt with Start->C while the others keep transitioning till Start->C is done
|
|
{
|
|
10.0f/*transitionLeftBlendTime*/,
|
|
1.0f/*transitionLeftCountDownTime*/,
|
|
10.0f/*transitionMiddleBlendTime*/,
|
|
2.0f/*transitionMiddleCountDownTime*/,
|
|
5.0/*transitionRightBlendTime*/,
|
|
3.0f/*transitionRightCountDownTime*/,
|
|
{
|
|
{
|
|
0/*_frameNr*/,
|
|
false/*stateA*/, false/*stateB*/, false/*stateC*/,
|
|
false/*transitionLeft*/, false/*transitionMiddle*/, false/*transitionRight*/,
|
|
0/*numStatesEntering*/, 0/*numStatesEntered*/,
|
|
0/*numStatesExited*/, 0/*numStatesEnded*/,
|
|
0/*numTransitionsStarted*/, 0/*numTransitionsEnded*/
|
|
},
|
|
{
|
|
60, // Start transition: Start->A
|
|
true, false, false,
|
|
true, false, false,
|
|
1, 0,
|
|
1, 0,
|
|
1, 0
|
|
},
|
|
{
|
|
90,
|
|
true, false, false,
|
|
true, false, false,
|
|
1, 0,
|
|
1, 0,
|
|
1, 0
|
|
},
|
|
{
|
|
120, // Interrupt transition: Start->A and start transition Start->B
|
|
true, true, false,
|
|
true, true, false,
|
|
2, 0,
|
|
2, 0,
|
|
2, 0
|
|
},
|
|
{
|
|
150,
|
|
true, true, false,
|
|
true, true, false,
|
|
2, 0,
|
|
2, 0,
|
|
2, 0
|
|
},
|
|
{
|
|
300, // Interrupt transition: Start->B and start transition Start->C
|
|
true, true, true,
|
|
true, true, true,
|
|
3, 0,
|
|
3, 0,
|
|
3, 0
|
|
},
|
|
{
|
|
330,
|
|
true, true, true,
|
|
true, true, true,
|
|
3, 0,
|
|
3, 0,
|
|
3, 0
|
|
},
|
|
{
|
|
480,
|
|
false, false, true,
|
|
false, false, false,
|
|
3, 3,
|
|
3, 3,
|
|
3, 3
|
|
}
|
|
}
|
|
},
|
|
// Start transition Start->A and let Start->B/C interrupt it
|
|
// Start->A/B finishes and holds the target state active while Start->C is finishing
|
|
{
|
|
2.0f/*transitionLeftBlendTime*/,
|
|
1.0f/*transitionLeftCountDownTime*/,
|
|
3.0f/*transitionMiddleBlendTime*/,
|
|
2.0f/*transitionMiddleCountDownTime*/,
|
|
10.0/*transitionRightBlendTime*/,
|
|
4.0f/*transitionRightCountDownTime*/,
|
|
{
|
|
{
|
|
0,
|
|
false, false, false,
|
|
false, false, false,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0
|
|
},
|
|
{
|
|
60, // Start transition: Start->A
|
|
true, false, false,
|
|
true, false, false,
|
|
1, 0,
|
|
1, 0,
|
|
1, 0
|
|
},
|
|
{
|
|
90,
|
|
true, false, false,
|
|
true, false, false,
|
|
1, 0,
|
|
1, 0,
|
|
1, 0
|
|
},
|
|
{
|
|
120, // Interrupt transition: Start->A and start transition Start->B
|
|
true, true, false,
|
|
true, true, false,
|
|
2, 0,
|
|
2, 0,
|
|
2, 0
|
|
},
|
|
{
|
|
150,
|
|
true, true, false,
|
|
true, true, false,
|
|
2, 0,
|
|
2, 0,
|
|
2, 0
|
|
},
|
|
{
|
|
180, // Start->A finishes and stays on the transition stack to keep the target state active
|
|
true, true, false,
|
|
true, true, false,
|
|
2, 0,
|
|
2, 0,
|
|
2, 0
|
|
},
|
|
{
|
|
240, // Interrupt transition Start->B with Start->C
|
|
true, true, true,
|
|
true, true, true,
|
|
3, 0,
|
|
3, 0,
|
|
3, 0
|
|
},
|
|
{
|
|
300, // Transition Start->B finishes and stays on the transition stack to keep the target state active
|
|
true, true, true,
|
|
true, true, true,
|
|
3, 0,
|
|
3, 0,
|
|
3, 0
|
|
},
|
|
{
|
|
330,
|
|
true, true, true,
|
|
true, true, true,
|
|
3, 0,
|
|
3, 0,
|
|
3, 0
|
|
},
|
|
{
|
|
840, // Latest active transition finishes and cleared the transition stack
|
|
false, false, true,
|
|
false, false, false,
|
|
3, 3,
|
|
3, 3,
|
|
3, 3
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
INSTANTIATE_TEST_CASE_P(AnimGraphStateMachine_InterruptionTest,
|
|
AnimGraphStateMachine_InterruptionFixture,
|
|
::testing::ValuesIn(animGraphStateMachineInterruptionTestData)
|
|
);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct AnimGraphStateMachine_InterruptionPropertiesTestData
|
|
{
|
|
float m_transitionLeftBlendTime;
|
|
float m_transitionLeftCountDownTime;
|
|
float m_transitionRightBlendTime;
|
|
float m_transitionRightCountDownTime;
|
|
AnimGraphStateTransition::EInterruptionMode m_interruptionMode;
|
|
float m_maxBlendWeight;
|
|
AnimGraphStateTransition::EInterruptionBlendBehavior m_interruptionBlendBehavior;
|
|
};
|
|
|
|
class AnimGraphStateMachine_InterruptionPropertiesFixture
|
|
: public AnimGraphFixture
|
|
, public ::testing::WithParamInterface<AnimGraphStateMachine_InterruptionPropertiesTestData>
|
|
{
|
|
public:
|
|
void ConstructGraph() override
|
|
{
|
|
const AnimGraphStateMachine_InterruptionPropertiesTestData param = GetParam();
|
|
AnimGraphFixture::ConstructGraph();
|
|
|
|
/*
|
|
+---+ +---+
|
|
| A | | B |
|
|
+-+-+ +-+-+
|
|
^ ^
|
|
| +---+---+ |
|
|
+----+ Start +----+
|
|
+-------+
|
|
*/
|
|
m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
|
|
m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
|
|
|
|
AnimGraphNode* stateStart = aznew AnimGraphMotionNode();
|
|
m_rootStateMachine->AddChildNode(stateStart);
|
|
m_rootStateMachine->SetEntryState(stateStart);
|
|
|
|
AnimGraphNode* stateA = m_motionNodeAnimGraph->GetMotionNodeA();
|
|
AnimGraphNode* stateB = m_motionNodeAnimGraph->GetMotionNodeB();
|
|
|
|
// Start->A (can be interrupted)
|
|
m_transitionLeft = AddTransitionWithTimeCondition(stateStart,
|
|
stateA,
|
|
param.m_transitionLeftBlendTime,
|
|
param.m_transitionLeftCountDownTime);
|
|
m_transitionLeft->SetCanBeInterrupted(true);
|
|
m_transitionLeft->SetInterruptionMode(param.m_interruptionMode);
|
|
m_transitionLeft->SetMaxInterruptionBlendWeight(param.m_maxBlendWeight);
|
|
m_transitionLeft->SetInterruptionBlendBehavior(param.m_interruptionBlendBehavior);
|
|
|
|
// Start->B (interrupting transition)
|
|
m_transitionRight = AddTransitionWithTimeCondition(stateStart,
|
|
stateB,
|
|
param.m_transitionRightBlendTime,
|
|
param.m_transitionRightCountDownTime);
|
|
m_transitionRight->SetCanInterruptOtherTransitions(true);
|
|
|
|
m_motionNodeAnimGraph->InitAfterLoading();
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
AnimGraphFixture::SetUp();
|
|
m_animGraphInstance->Destroy();
|
|
m_animGraphInstance = m_motionNodeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet);
|
|
}
|
|
|
|
AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
|
|
AnimGraphStateTransition* m_transitionLeft = nullptr;
|
|
AnimGraphStateTransition* m_transitionRight = nullptr;
|
|
};
|
|
|
|
TEST_P(AnimGraphStateMachine_InterruptionPropertiesFixture, AnimGraphStateMachine_InterruptionPropertiesTest)
|
|
{
|
|
bool prevGotInterrupted = false;
|
|
float prevBlendWeight = 0.0f;
|
|
|
|
Simulate(2.0f /*simulationTime*/, 10.0f /*expectedFps*/, 0.0f /*fpsVariance*/,
|
|
/*preCallback*/[this]([[maybe_unused]] AnimGraphInstance* animGraphInstance) {},
|
|
/*postCallback*/[this]([[maybe_unused]] AnimGraphInstance* animGraphInstance) {},
|
|
/*preUpdateCallback*/[this](AnimGraphInstance*, float, float, int) {},
|
|
/*postUpdateCallback*/[this, &prevGotInterrupted, &prevBlendWeight](AnimGraphInstance* animGraphInstance, [[maybe_unused]] float time, [[maybe_unused]] float timeDelta, [[maybe_unused]] int frame)
|
|
{
|
|
const AnimGraphStateMachine_InterruptionPropertiesTestData param = GetParam();
|
|
|
|
const AnimGraphStateTransition::EInterruptionMode interruptionMode = m_transitionLeft->GetInterruptionMode();
|
|
const float maxInterruptionBlendWeight = m_transitionLeft->GetMaxInterruptionBlendWeight();
|
|
const bool gotInterrupted = m_transitionLeft->GotInterrupted(animGraphInstance);
|
|
const bool gotInterruptedThisFrame = gotInterrupted && !prevGotInterrupted;
|
|
const float blendWeight = m_transitionLeft->GetBlendWeight(animGraphInstance);
|
|
|
|
if (m_transitionLeft->GetInterruptionMode() == AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight)
|
|
{
|
|
if (blendWeight > maxInterruptionBlendWeight)
|
|
{
|
|
EXPECT_FALSE(gotInterruptedThisFrame) << "No interruption should be possible anymore at blend weight " << blendWeight << ".";
|
|
}
|
|
else
|
|
{
|
|
if (m_transitionRight->CheckIfIsReady(animGraphInstance))
|
|
{
|
|
EXPECT_TRUE(gotInterrupted) << "Interruption should have happened.";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gotInterrupted && !gotInterruptedThisFrame &&
|
|
m_transitionLeft->GetInterruptionBlendBehavior() == AnimGraphStateTransition::EInterruptionBlendBehavior::Stop)
|
|
{
|
|
EXPECT_FLOAT_EQ(prevBlendWeight, blendWeight) << "The blend weight should not change anymore after the transition got interrupted when using blend behavior stop.";
|
|
}
|
|
|
|
prevGotInterrupted = gotInterrupted;
|
|
prevBlendWeight = blendWeight;
|
|
}
|
|
);
|
|
}
|
|
|
|
std::vector<AnimGraphStateMachine_InterruptionPropertiesTestData> animGraphStateMachineInterruptionPropertiesTestData
|
|
{
|
|
// Enable right transition at 0.5 while this is over the max blend weight already, don't allow interruption.
|
|
{
|
|
1.0f /*transitionLeftBlendTime*/,
|
|
0.0f /*transitionLeftCountDownTime*/,
|
|
1.0f /*transitionRightBlendTime*/,
|
|
0.5f /*transitionRightCountDownTime*/,
|
|
AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight /*interruptionMode*/,
|
|
0.1f /*maxBlendWeight*/,
|
|
AnimGraphStateTransition::EInterruptionBlendBehavior::Continue /*interruptionBlendBehavior*/
|
|
},
|
|
// Right transition ready after 0.5 while still in range for the max blend weight, interruption expected.
|
|
{
|
|
1.0f,
|
|
0.0f,
|
|
1.0f,
|
|
0.5f,
|
|
AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight,
|
|
0.6f,
|
|
AnimGraphStateTransition::EInterruptionBlendBehavior::Continue
|
|
},
|
|
// Interruption always allowed.
|
|
{
|
|
0.5f,
|
|
0.0f,
|
|
0.5f,
|
|
0.2f,
|
|
AnimGraphStateTransition::EInterruptionMode::MaxBlendWeight,
|
|
1.0f,
|
|
AnimGraphStateTransition::EInterruptionBlendBehavior::Continue
|
|
},
|
|
// Test if interrupted transitions stop transitioning with blend behavior set to stop.
|
|
{
|
|
1.0f,
|
|
0.0f,
|
|
1.0f,
|
|
0.5f,
|
|
AnimGraphStateTransition::EInterruptionMode::AlwaysAllowed,
|
|
0.0f,
|
|
AnimGraphStateTransition::EInterruptionBlendBehavior::Stop
|
|
},
|
|
};
|
|
|
|
INSTANTIATE_TEST_CASE_P(AnimGraphStateMachine_InterruptionPropertiesTest,
|
|
AnimGraphStateMachine_InterruptionPropertiesFixture,
|
|
::testing::ValuesIn(animGraphStateMachineInterruptionPropertiesTestData)
|
|
);
|
|
} // EMotionFX
|