UCharacterMovementComponent crashes because Velocity contains NaN

So I spent quite a bit of time looking into this issue and made a fix for it in our code that overrides the engine’s code.

Here is what I discovered:
The crash occurs in this function:

void UCharacterMovementComponent::PerformMovement(float DeltaSeconds)

On this line:

checkf(!Velocity.ContainsNaN(), TEXT("UCharacterMovementComponent::PerformMovement: Velocity contains NaN (%s: %s)\n%s"), *GetPathNameSafe(this), *GetPathNameSafe(GetOuter()), *Velocity.ToString());

So I would assume that whoever added that line is probably really interested as to the cause of this issue. I discovered it became NaN after calling this function:

ApplyAccumulatedForces(DeltaSeconds);

In this function I discovered that the PendingImpulseToApply variable contained a NaN in the Z axis. This is only set in the AddImpulse function so I set a breakpoint to find out where / when it occurred and the NaN occurred coming from this function:

void ACharacter::ApplyDamageMomentum(float DamageTaken, FDamageEvent const& DamageEvent, APawn* PawnInstigator, AActor* DamageCauser)

Then digging down deeper it came from:

DamageEvent.GetBestHitInfo(this, PawnInstigator, HitInfo, ImpulseDir);

And ImpulseDir is set by this:

OutImpulseDir = HitInstigator ? 
			( OutHitInfo.ImpactPoint - HitInstigator->GetActorLocation() ).GetSafeNormal()
			: FVector::ZeroVector;

So in case someone wants to fix it, that is where your NaN is coming from and causing crashes. In our code I overrode the Character’s ApplyDamageMomentum function and checked if it was ContainsNaN and if so I set it to 0.0f like this (see the comment (XXX Custom code) guards):

void AXXXSoldier::ApplyDamageMomentum(float DamageTaken, FDamageEvent const& DamageEvent, APawn* PawnInstigator, AActor* DamageCauser)
{
	UDamageType const* const DmgTypeCDO = DamageEvent.DamageTypeClass->GetDefaultObject<UDamageType>();
	float const ImpulseScale = DmgTypeCDO->DamageImpulse;

	if ((ImpulseScale > 3.0f) && (CharacterMovement != nullptr))
	{
		FHitResult HitInfo;
		FVector ImpulseDir;
		DamageEvent.GetBestHitInfo(this, PawnInstigator, HitInfo, ImpulseDir);

		/** XXX Custom code below **/
		if (ImpulseDir.ContainsNaN())
		{
#if !UE_BUILD_SHIPPING
			UE_LOG(LogActor, Error, TEXT("ApplyDamageMomentum ImpulseDir contains NaN (would have caused a crash):%s"), *ImpulseDir.ToCompactString());
#endif
			if (FMath::IsNaN(ImpulseDir.X) || !FMath::IsFinite(ImpulseDir.X))
			{
				ImpulseDir.X = 0.0f;
			}
			if (FMath::IsNaN(ImpulseDir.Y) || !FMath::IsFinite(ImpulseDir.Y))
			{
				ImpulseDir.Y = 0.0f;
			}
			if (FMath::IsNaN(ImpulseDir.Z) || !FMath::IsFinite(ImpulseDir.Z))
			{
				ImpulseDir.Z = 0.0f;
			}
		}
		/** XXX Custom code above **/

		FVector Impulse = ImpulseDir * ImpulseScale;
		bool const bMassIndependentImpulse = !DmgTypeCDO->bScaleMomentumByMass;

		// limit Z momentum added if already going up faster than jump (to avoid blowing character way up into the sky)
		{
			FVector MassScaledImpulse = Impulse;
			if (!bMassIndependentImpulse && CharacterMovement->Mass > SMALL_NUMBER)
			{
				MassScaledImpulse = MassScaledImpulse / CharacterMovement->Mass;
			}

			if ((CharacterMovement->Velocity.Z > GetDefault<UCharacterMovementComponent>(CharacterMovement->GetClass())->JumpZVelocity) && (MassScaledImpulse.Z > 0.0f))
			{
				Impulse.Z *= 0.5f;
			}
		}

		CharacterMovement->AddImpulse(Impulse, bMassIndependentImpulse);
	}
}

Hi ,

Thank you for the report. We have assigned one of our technicians to investigate your issue and they may post here with additional questions or comments.

Could you also include the full Callstack from the crashreport, log, and dmp files.

Hey ,

As mentioned, could you provide the callstack from the crash as well as the project’s log and dmp files? Additionally, can you explain what you were doing at the time of the crash (working inside blueprints, play in editor, etc).

Cheers

Hi and TJ, I have provided enough information in the post that you do not need the call stack or dump files. I always run against engine source hooked into the debugger, hence I do not get dump files. Please the entire post and you will clearly see that you have more than enough information to fix this problem. I pointed you to the exact problem and I have fixed it on our side. If either of you cannot figure it out please forward this on to Tim .
Thanks,

Hey ,

Having information about the crash itself (callstack and logs) helps determine not only where the crash is occurring but what exactly is causing it. If you are unable to provide this information, and since you seem to have found a solution for your project, I would highly recommend entering a pull request to have your fix included in the engine (About pull requests - GitHub Docs).

Cheers

Hi , I’m seriously wondering if you the original post. The location of the crash is a symptom of the problem. The reason (original cause) for the crash is what I investigated and posted above along with a work-around. The call stack is not important as it gives you no further information than what I have already provided in the original post. The crash is the result of this being set improperly:

OutImpulseDir = HitInstigator ? ( OutHitInfo.ImpactPoint - HitInstigator->GetActorLocation() ).GetSafeNormal() : FVector::ZeroVector;

But the above is set long before anything in the call stack gets called as it is stored within member variables and later retrieved. Once it is retrieved and used to calculate the velocity it will then check if the Velocity contains NaN and then crashes on the check call inside of the PerformMovement function.

All of this was clearly stated in the original post and logs and the call stack of the crash will not help you to fix this particular problem because it is the symptom of a problem. I guess if you choose to not create a ticket for it, then when others complain about this issue they will find this answerhub post in the future and they can create a ticket for it in the future or you could just create one now as everything is clearly, very clearly, stated in the original post. If you really, really “NEED” the call stack then I can provide one and then you can publicly acknowledge that it doesn’t help you at all in solving this issue because I already dug into the issue and reported it above in the original post. Please actually the original post this time as I do not have the time to fix this issue via a pull request as I have done with others because we have a work-around that we can use in our project.

Hey -

I’ve entered a report (UE-20231) to investigate your possible fix for this issues.

Cheers

OK, thanks , it is much appreciated, although I wouldn’t consider my fix to fix this issue, it is just a work-around until someone fixes the engine code because I’m sure this is called elsewhere.
Thanks,

Hi , thanks for reporting this.

Do you have any more info on where specifically the NaN was coming from? GetBestHitInfo is virtual and implemented in a few damage types, are you sure it’s the genereic FDamageEvent version you pasted code for, and not FPointDamageEvent or FRadialDamageEvent?

Without a repro it’s hard to say where the NaN came from, based on the code you pasted it would have been in “OutHitInfo.ImpactPoint - HitInstigator->GetActorLocation() ).GetSafeNormal()” but that just means either the ImpactPoint was filled in with bad data, or the actor got corrupted somehow.