Checking the movement component work fine if you just care about giving some set amount of damage on falling. You could also calculate based on the change in velocity if you wanted something more fine-grained. In my current game, gravity is not consistent. The same fall on the surface of Mars (gravity .38g) or on Earth (1g) need to yield different amounts of damage, so I wanted a solution based on change of velocity.
In the header.h:
//////////////////////////////////////////////////////////////////////////
// Damage
/** Identifies this character should take damage if falling */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Movement|Falling")
bool bCanTakeFallDamage;
/** This is the maximum change in velocity that a character can experience
* without taking damage. Defaults to 1000.
*
* _It's not the fall that kills you, it's the sudden stop at the end._
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Movement|Falling")
float SafeFallVelocity;
/** How much damage to deal to this character when the stop is greater than
* SafeFallVelocity. The actually delta velocity minus SafeFallVelocity will
* multiplied by this value and then applied as damage. Defaults to .1;
**/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Movement|Falling")
float FallDamageMultiplier;
protected:
FVector PreviousVelocity;
And in the implementation.cpp:
void ABaseNPCCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
if (!bCanTakeFallDamage) return;
// We deal with some big numbers here to avoid using sqrtf() every tick
FVector Velocity = GetCharacterMovement()->Velocity;
FVector DeltaVelocitySquared = (Velocity - PreviousVelocity) * (Velocity - PreviousVelocity);
float DeltaSpeed = DeltaVelocitySquared.X + DeltaVelocitySquared.Y + DeltaVelocitySquared.Z;
if (DeltaSpeed > (SafeFallVelocity * SafeFallVelocity))
{
// In this case, we have a sudden stop, and one that's sudden enough to cause damage. This should be rare enough that we can afford a sqrtf()
float Damage = sqrtf(DeltaSpeed) * FallDamageMultiplier;
FDamageEvent DamageEvent;
TakeDamage(Damage, DamageEvent, GetController(), this);
}
PreviousVelocity = Velocity;
}
Definitely room for improvements, but it provides a pretty good approximation of reality if configured properly. Here are my default values from the constructor:
ABaseNPCCharacter::ABaseNPCCharacter()
{
PreviousVelocity = FVector(0.f, 0.f, 0.f);
bCanTakeFallDamage = false;
SafeFallVelocity = 1000.f;
FallDamageMultiplier = .1f;
}