Trying to access GetRelevantAssetPlayerFromState crashes the game

I am trying to play scripted sequences in the animation blueprint. For example, an item has what animation it wants to be used set in its blueprint and I want the character to play that animation but still get the benefits of animation blueprints (stuff like head rotation in the Anim Graph).

The problem is that the access to what the blueprint is doing or what animation is it playing is really tricky. I had to implement my own UAnimInstance. It was working fine for a little while but now it has started to crash the game.

How it works:

I call AEntruliaCharacter::PlayAnimation() which sets ScriptedAnimation. In the animation blueprint if the character has valid ScriptedAnimation then it will go to the “Scripted” state. In the item C++ code I will check if the character has finished the animation. This is a bit complicated.

**1.: ** I need to grab the UEntruliaAnimInstance and find the “Scripted” Machine. I check that it has valid index (I am not sure what is a valid index but I assume it’s >= 0) and that its current state index is also valid (I also assume >= 0).

2.: I make sure the current State is “Scripted”. This will return the FAnimNode_StateMachine pointer if it’s all correct.

**3.: ** I need to check that the state is actually playing the right scripted animation and not an old one. For this I need to call GetRelevantAssetPlayerFromState() which I feed the Machine index and current state index. It will then return FAnimNode_AssetPlayerBase which I ask to give GetAnimAsset();

4.: If the animation pointer matches my ScriptedAnimation I know I am playing the right animation. If it returns NULL or mismatching animation I will have “IsAnimationFinished” just return false. Then I need to call GetRelevantAnimTimeRemaining() with the machine index and current state index to see if the animation is over. This is similar to what you would do in a blueprint.

But currently it crashes part 3 with GetRelevantAssetPlayerFromState(). I have tried turning off multithreaded animation blueprints and accessing different versions of GetRelevantAssetPlayerFromState(). I have also tried disabling the check if the animation matches ScriptedAnimation but even calling GetRelevantAnimTimeRemaining() seems to crash the editor.



UE4Editor_Entrulia_295!UEntrulianAnimInstance::GetScriptedState() [f:\ffentrulia\source\entrulia\ai\entruliananiminstance.cpp:65]
UE4Editor_Entrulia_295!AEntruliaCharacter::IsAnimationFinished() [f:\ffentrulia\source\entrulia\entruliacharacter.cpp:2016]
UE4Editor_Entrulia_295!AEnterableMachine::RunEnterableTick() [f:\ffentrulia\source\entrulia\interactive\enterablemachine.cpp:424]
UE4Editor_Entrulia_295!AEntruliaCharacter::Tick() [f:\ffentrulia\source\entrulia\entruliacharacter.cpp:3995]


My UAnimInstance derived class:



//=================================================================
// 
//=================================================================
UEntrulianAnimInstance::UEntrulianAnimInstance(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{

}

//=================================================================
// 
//=================================================================
class UAnimationAsset *UEntrulianAnimInstance::GetAnimation(int32 MachineIndex, int32 StateIndex)
{

	FAnimNode_AssetPlayerBase *AnimNode = GetRelevantAssetPlayerFromState(MachineIndex, StateIndex);
	if (!AnimNode)
		return NULL;

	return AnimNode->GetAnimAsset();
}

//Both the Machine and the State are called this because I am lazy
static const FName Name_Scripted = TEXT("Scripted");

//=================================================================
// 
//=================================================================
FAnimNode_StateMachine *UEntrulianAnimInstance::GetScriptedState_Internal()
{
	FAnimNode_StateMachine *pScripted = GetStateMachineInstanceFromName(Name_Scripted);
	if (pScripted == NULL)
		return NULL;

	if (pScripted->StateMachineIndexInClass < 0)
		return NULL;

	if (pScripted->GetCurrentState() < 0)
		return NULL;

	if (!pScripted->GetCurrentStateName().IsEqual(Name_Scripted))
		return NULL;

	return pScripted;
}

//=================================================================
// This gets the state machine for the player but only if it has the right animation running
//=================================================================
FAnimNode_StateMachine *UEntrulianAnimInstance::GetScriptedState(class UAnimSequence *ScriptedAnimation)
{
	if (!ScriptedAnimation)
		return NULL;

	FAnimNode_StateMachine *pScripted = GetScriptedState_Internal();
	if (pScripted == NULL)
		return NULL;

	class UAnimationAsset *pAnim = GetAnimation(pScripted->StateMachineIndexInClass, pScripted->GetCurrentState());
	if (pAnim != ScriptedAnimation)
		return NULL;

	return pScripted;
}

//=================================================================
// 
//=================================================================
void UEntrulianAnimInstance::ResetAnimation()
{
	FAnimNode_StateMachine *pScripted = GetScriptedState_Internal();
	if (!pScripted)
		return;

	FAnimNode_AssetPlayerBase *AnimNode = GetRelevantAssetPlayerFromState(pScripted->StateMachineIndexInClass, pScripted->GetCurrentState());
	if (!AnimNode)
		return;

	AnimNode->ClearCachedBlendWeight();
	AnimNode->SetAccumulatedTime(0.0f);
}

Relevant parts of my Character derived class:



//=================================================================
// 
//=================================================================
void AEntruliaCharacter::PlayAnimation(class UAnimSequence *Animation, bool Loop)
{
	bool bReset = ScriptedAnimation != NULL;

	ScriptedAnimation = Animation;
	LoopAnimation = Loop;

	if (!bReset)
		return;

	class UEntrulianAnimInstance *pInstance = Cast<UEntrulianAnimInstance>(GetMesh()->GetAnimInstance());
	if (!pInstance)
		return;

	pInstance->ResetAnimation();
}

//=================================================================
// 
//=================================================================
bool AEntruliaCharacter::IsAnimationFinished() const
{
	if (!GetMesh())
		return false;

	class UEntrulianAnimInstance *pInstance = Cast<UEntrulianAnimInstance>(GetMesh()->GetAnimInstance());
	if (!pInstance)
		return false;

	FAnimNode_StateMachine *pScripted = pInstance->GetScriptedState(ScriptedAnimation);
	if (!pScripted)
		return false;

	float flTimeRemaining = pInstance->GetRelevantAnimTimeRemaining(pScripted->StateMachineIndexInClass, pScripted->GetCurrentState());
	return flTimeRemaining < 0.05f;
}

//=================================================================
// 
//=================================================================
float AEntruliaCharacter::GetAnimationFraction() const
{
	if (!GetMesh())
		return 0.0f;

	class UEntrulianAnimInstance *pInstance = Cast<UEntrulianAnimInstance>(GetMesh()->GetAnimInstance());
	if (!pInstance)
		return 0.0f;

	FAnimNode_StateMachine *pScripted = pInstance->GetScriptedState(ScriptedAnimation);
	if (!pScripted)
		return 0.0f;

	return pInstance->GetRelevantAnimTimeFraction(pScripted->StateMachineIndexInClass, pScripted->GetCurrentState());
}


569398338c56f56134a605be15eebe07b7868a6e.jpeg