Unreal Engine 4: Simple Fall Damage
In this small tutorial I want to show you, how to create a simple fall distance based fall damage system in Unreal Engine 4 using C++ and Blueprint.
But before we begin, we need to setup our project:
The Project Setup
For demonstration purposes I use the default Third Person Example Project as our base. Create a C++ project without starter content.
When the project finished loading we will add a FloatCurve. Right click in your Content Browser and under Miscellaneous select Curve. Next select the CurveFloat and hit “Select”. Name the new curve “C_FallDamage” and open it.
Here you can modify the fall damage. The X-Axis is equal to the FallDistance in WorldUnits (default: 1cm) and the Y-Axis is equal to the fall damage the character should recieve. As an example we want to add 4 keys to the curve. One at (0.0, 0.0), one at (200.0, 10.0), one at (999.0, 20.0) and the last one at (1000.0, 999.0). The last key is assuming that 999HP is the maximum a character can have and will result in death if the character is falling more than 1000cm (10m)
Next, we want to create a custom Damage Type, that is used when applying fall damage. For this, add a new Blueprint Class with “DamageType” as its parent class (extend all classes at the bottom) and name it “DT_Falling”.
Now we can start creating our custom UCharacterMovementComponent.
The CharacterMovementComponent
First we will create a new C++ class: Select under File>Project>New C++ Class… and then check “Show All Classes”. Now select “CharacterMovementComponent”. Provide any name of your choosing, I will go with the default “MyCharacterMovementComponent”. Click the “Public” button on the right and change the path to [...]/Public/Framework/Components. Next click “Create Class” and wait for the project to compile.
First open MyCharacterMovementComponent.h and declare a dynamic multicast delegate above the class declaration. This we will use to handle the actual applying of the fall damage to our character.
// Event Handler for Death DataDECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnFallDamage, float, FallDamage, TSubclassOf< class UDamageType >, FallDamageTypeClass);
Next we want to declare a few variables.
UPROPERTY(BlueprintAssignable, Category = "Events")FOnFallDamage OnFallDamage;
This is our reference to the dynamic multicast delegate, we declared earlier.
UPROPERTY(Category="Character Movement: Jumping / Falling", EditAnywhere, BlueprintReadWrite)UCurveFloat* FallDamageCurve;
We will use the FallDamageCurve to calculate our fall damage using an easy to modify Float Curve.
UPROPERTY(Category="Character Movement: Jumping / Falling", EditAnywhere, BlueprintReadWrite)TSubclassOf< class UDamageType > FallDamageTypeClass;
The FallDamageTypeClass will be the damage type that we will later pass down to the ApplyDamage function.
UPROPERTY(Category="Character Movement: Jumping / Falling", EditAnywhere, BlueprintReadWrite)float FallDamageKillDuration;
This will be the maximum fall duration after which our charcter will receive lethal damage. While our fall damage calculation will be distance based, this will prevent the charcter from endlessly falling (due to an bug for example).
/* The duration the character is falling */float CurrentFallingDuration;
This will store the duration the character is currently falling.
/* The highest point while falling */FVector HighestFallingPoint;
And finally this will be the highest point in our fall, used to calculate the fall distance.
Next we want to declare the Constructor, the TickComponent function and the ProcessLanded function.
UMyCharacterMovementComponent();virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;virtual void ProcessLanded(const FHitResult& Hit, float remainingTime, int32 Iterations) override;
Now we move on to our MyCharacterMovementComponent.cpp file:
Get Niklas Vlach’s stories in your inbox
Join Medium for free to get updates from this writer.
Here we first will add our declared functions:
UMyCharacterMovementComponent::UMyCharacterMovementComponent()
{
}void UMyCharacterMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}void UMyCharacterMovementComponent::ProcessLanded(const FHitResult& Hit, float remainingTime, int32 Iterations)
{
Super::ProcessLanded(Hit, remainingTime, Iterations);
}
First our constructor, here we set the FallDamageKillDuration to 5 seconds.
UMyCharacterMovementComponent::UMyCharacterMovementComponent()
{
FallDamageKillDuration = 5.f;
}Next inside our TickComponent function we will check if we are falling and if we are falling, we first update our CurrentFallingDuration by adding our DeltaTime to our CurrentFallingDuration.
// update fall duration
CurrentFallingDuration += DeltaTime;Next we will update our HighestFallingPoint. First we will check if the HighestFallingPoint is the Zero Vector or if the actors current z coordinate is higher than the current HighestFallingPoint z coordinate, if so we set the HighestFallingPoint to our actor location.
// update highest position, while falling (used in damage calculation)
if( HighestFallingPoint.IsZero() || GetOwner()->GetActorLocation().Z > HighestFallingPoint.Z )
{
HighestFallingPoint = GetOwner()->GetActorLocation();
}Next we check if our CurrentFallingDuration is greater or equal to our FallDamageKillDuration, then we apply lethal damage by calling our OnFallDamage Delegate.
// kill character if falling too long (prevent getting stuck)
if( CurrentFallingDuration >= FallDamageKillDuration )
{
OnFallDamage.Broadcast(999999.f, FallDamageTypeClass);
}Our complete TickComponent function should now look like this:
void UMyCharacterMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if( IsFalling() )
{
// update fall duration
CurrentFallingDuration += DeltaTime; // update highest position, while falling (used in damage calculation)
if( HighestFallingPoint.IsZero() || GetOwner()->GetActorLocation().Z > HighestFallingPoint.Z )
{
HighestFallingPoint = GetOwner()->GetActorLocation();
} // kill character if falling too long (prevent getting stuck) if( CurrentFallingDuration >= FallDamageKillDuration )
{
OnFallDamage.Broadcast(999999.f, FallDamageTypeClass);
}
}
}
Next we will move on to our ProcessLanded function. Here we will first calculate our FallHeight
// calculate fall height
float FallHeight = HighestFallingPoint.Z - GetOwner()->GetActorLocation().Z;Next we reset our CurrentFallingDuration and our HighestFallingPoint as we no longer need them for this fall.
// reset fall variables
CurrentFallingDuration = 0;
HighestFallingPoint = FVector(0,0,0);Next we will calculate our fall damage using our CurveFallDamage.
// calculate fall damage
float FallDamage = CurveFallDamage->GetFloatValue( FallHeight );And last but certainly not least we call our OnFallDamage Delegate
if( FallDamage > 0 )
{
OnFallDamage.Broadcast(FallDamage, FallDamageTypeClass);
}Our whole ProcessLanded function should now look like this:
void UMyCharacterMovementComponent::ProcessLanded(const FHitResult& Hit, float remainingTime, int32 Iterations)
{
Super::ProcessLanded(Hit, remainingTime, Iterations); // calculate fall height
float FallHeight = HighestFallingPoint.Z - GetOwner()->GetActorLocation().Z; // reset fall variables
CurrentFallingDuration = 0;
HighestFallingPoint = FVector(0,0,0); // calculate fall damage [percentage]
float FallDamage = CurveFallDamage->GetFloatValue( FallHeight ); // broadcast damage event
if( FallDamage > 0 )
{
OnFallDamage.Broadcast(FallDamage, FallDamageTypeClass);
}
}
And with this, we have our CharacterMovementComponent completed.
The Character
Next we need to tell our Character to use our MyCharacterMovementComponent instead of the default one. I will show the setup using the default PlayerCharacter, created by the Third Person Example C++ Project but of course this component can be added to any other Character.
First we need to change our Constructor from
ACustomCharacter();to
ACustomCharacter(const class FObjectInitializer& ObjectInitializer);in the Characters .h header file and add a new function declaration
UFUNCTION()
void OnFallDamage(float FallDamagePercentage, TSubclassOf< class UDamageType > FallDamageTypeClass);In our C++ File we need to tell the Character to use our Component using aObjectInitialiser.SetDefaultSubobjectClass() . For this add
#include "Framework/Components/MyCharacterMovementComponent.h"at the end of your includes at the top of the file and change the Constructor from
ACustomCharacter::ACustomCharacter()
{
//...
}to
ACustomCharacter::ACustomCharacter(const class FObjectInitializer& ObjectInitializer)
:Super(ObjectInitializer.SetDefaultSubobjectClass<UMyCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))
{
//...
}Now we need to add our character to our OnFallDamage Delegate by adding
UMyCharacterMovementComponent* Movement = Cast<UMyCharacterMovementComponent>( GetMovementComponent() );
if( Movement )
{
Movement->OnFallDamage.AddDynamic(this, &ACustomCharacter::OnFallDamage);
}to the constructor.
Finally we need to implement the OnFallDamage function in our character, which is fairly simple:
void ASLCharacterBase::OnFallDamage(TSubclassOf< class UDamageType > FallDamageTypeClass, float FallDamagePercentage)
{
float FallDamage = GetCharacterStatsComponent()->GetMaxHealth() * FallDamagePercentage;
UGameplayStatics::ApplyDamage(this, FallDamage, NULL, NULL, FallDamageTypeClass);
}Now we can use the AnyDamage event to handle the health of our character, either in C++ or Blueprint.
Now compile your project and we can move on to the last step.
The Configuration
Open your Character Blueprint and select the Character Movement Component. Under “Character Movement: Jumping / Falling” select our “C_FallDamge” as our Curve Fall Damage and DT_Falling as our Fall Damage Type Class. And now have fun with your new fall damage.
Epilogue
While you could apply the damge directly inside the MyCharacterMovementComponent, I chose to pass it down to the character itself, so that I could easily add systems to the project without having to much refactoring (like a magic spell, that reduces fall damage).
I hope my first tutorial was helpfull to you and any advice and improvement ideas are welcome.




