Is this Efficient: Setting Up Fall Damage System

I’m starting to set up a fall damage system. Was going to go by velocity but my numbers are bonkers due to player speed so I decided to use height. I have a few questions about how I’m set up here.

First question would be is the “Bind event to on Reached Jump Apex” the best way to measure the top of a jump?


I use the “jump apex” and “Event On Walking Off Ledge” to set the actors z location for start height. Is “Get Actor Location” the best way to measure this? For anyone curious about why I’m dividing by 30: About 30.48cm is equal to 1 foot. I’m using 15, 20 and 25 feet to draw my damage numbers.

I’m just testing the waters here for drawing damage numbers. I’m doing this in a function. My question is, rather than doing hard numbers like this how could I scale the damage according to the fall distance. I don’t necessarily need a concrete tutorial, a starting point would fine.

Hi, why don’t you just use the z-velocity on landed to determine the damage (using some float curve)?

​​​​This is the method I’ve been using for fall damage and it seems to work just fine, minus it being on a tick, which I know you should avoid having too many things on a tick event. The reason it is on a tick event, is because this is inside an actor component that can be placed on ANY character. I wanted to avoid using On Landed or any other pre-generated UE event dispatchers to achieve this.

There are numerous ways it could be done it just depends on how the user (you) goes about setting it up and implementing it.

I’ll read up on them, new to this so I’m not too sure what options are available for accomplishing stuff.

Thank for the examples :slight_smile:
I watched the UE talk that briefly covered ticks, do they hurt the performance of MP games much?

Binding OnReachedJumpApex event is a good way to check the peak of a jump. The NotifyApex bool must be turned on when a jump is performed to trigger the event (sets it to false when the event fires).

I agree with chrudimer, velocity is a better approach, here’s a hint:
Screenshot 2021-01-21 192649.jpg
You probably want the up vector instead, but this will return the velocity of whichever vector. FYI, you will want a negative result because it’s the up vector!

I typically try to solve things without tick, delays, or timers if possible, but if it has to be a continuous check, timers are the most flexible and work like a dedicated tick (and you can have multiple!)
Here’s how I set up my timers:


Call “custom event 0” to start the timer. Clear the timer handle in the timer’s “custom event 1” when you want to stop it (or wherever else that makes sense)

Another route is to compare the distance from when “walked off ledge” or “jumped” or whatever and where they land. If the distance between those vectors is greater than a certain threshold, they take damage based on the difference. Example: If they fall 1200 and the threshold is 1000, they should take damage of 200/ratio - where the ratio can be a flat number, linear variant, or from a curve table. You can measure just Z distance as well (there is already a node for it).

@iEatRazorz

Stick with Velocity (speed). It’s more performant and straightforward. The height of a fall isn’t what causes damage IRL. It’s the velocity at impact (negating mass etc etc).

Determining Velocity… There’s two approaches here depending on what you want to base velocity on. Either the character movement component (Get Velocity), or the physics linear velocity of a bone (root) at blocking hit impact location. Both are extremely easy. My preference for simplistic fall damage would be Get Velocity(split) → Z (ABSOLUTE) to return a Speed (float).

Once you have Speed the rest is pretty trivial.

  • Determine at what speed you want to start taking damage (0.001)
  • Determine at what speed to take max damage (100.00).

With these two values you can use Normalize to Range (float) to plot damage strictly based on speed value between your min and max. Multiply the return by 100 for damage (float).

Determining the two speed values above is academic. Set up blocks for the two heights you want to use … min height to take damage, min height for death. e.g. 5m (5000cm) and 20m (20000cm).
Drop from those heights and note the speed. Round those values. up/down, nearest 10 etc, is up to you.

Done.

Went ahead and wrote it out. Added a bit more for the sake of future scope.

Event On Landed will in the future be tasked with playing sound effects on impact, sfx, animations (montage[roll, heavy landing]) etc. Keep the event it clean!

The first function inline should be one that creates common/repetitive use variables. For this example I’m creating 3. Hit result, Location and Speed.

For future scope I figured WTH add a simple sound set up. Note *** I did say simple.

Calculate Damage

Apply Any Damage

Determining Min Speeds for damage based on height.

I used blocks height scaled from 5m, 10m, 15m, to 20m. Noting the speed at impact. I also did this for standard jump. Each avg value I rounded up a little for cushion. To ease the burden of collecting these speeds I created a simple teleporter to move me to the top of the corresponding block.

Teleporter BP
Uses a Box Collision to trigger and a target point sprite for teleport to location.

Teleporter Demo

Fall Damage Testing Demo

Thank you everyone. I now have plenty of new material to chew on and research after I finish up with some house electric and draywalling. I’ll post if I have problems or when I have the end result once I’m there

So I followed **Rev0verDrives **example after playing around with the examples provided here, I just want to make sure I actually understand what is going on.

Event Landed is used to pipe the results from the landing into the On Landed Function.

OLHit stores the hit result from the Event On Landed, we break up that to pull the relevant data. We then pull the velocity and ensure a positive number is stored. Why are we storing the location of the impact? Is that purely for debugging fall collision?

If I understand, Blocking Hit means movement has been brought to a complete stop on solid surface? Normalize to Range converts the value of a given range to a percentage in decimal form? We multiply by 100 to convert that to a full number? We clamp it to ensure it doesn’t exceed the given health range of the player? If the clamp is the case, why again in apply damage?

@iEatRazorz

On Landed is the event that will trigger execution. It’s triggered when is falling is changed to false. This is automated through CMC.

Location and Speed will be used quite often down the line for various other functions. You’ll see this as you continue development. So instead of piping a long line from the events hit result or constantly breaking hit result, we just set them as their own vars. It helps keep things clean. The naming schema OL_variable also helps in this regard.

Blocking hit refers to Collision…stops movement at impact.

Normalize to Range (NtR) will return a value based on a curve from min to max (0.0 to 1.0) without the multiply your max damage from any height would be 1.0

You can think of it as Alpha return from timeline.

e.g. Say our min/max speeds are 0 -> 400. at 100 NtR will return 0.25, at 200|0.5, at 300|0.75 and so forth.

Typically player health is a float and max health is 100.00. Thus multiplying the NtR return by 100 would result in 25.00, 50.00, 75.00 100.00 for the above example.

You can have your player heath be 1.0 and use the direct result of NtR.

This just keeps the numbers clean. For example if your Max NtR is 1200 and your impact speed is 1600, then you’ll have a greater than player max health damage.

e.g. player health max is 100, damage is 137.56

If you allow for negative values, then you have to code for their “potential” existence. This can add bloat where it’s not needed in the first place. There’s no point in ever subtracting a damage value from Player Health that’s greater than the players max health. The result is the same… death.

​​​​​​​

We clamp Player Health, so that we do not ever get a player health value below 0.00

If your game tracks Hit Points based on damage, then you need another variable to store that.

e.g. Raw damage of 137.56 -> hit points. Raw Damage (clamped) -> player health.

Thank you for the guidance Rev. Now I tackle climbing which I think I need to start with edge detection to accomplish.

You’ll need a few new collision object channels for this. Here’s a few decent tutorials on climbing, vaulting etc.

[quote=“Rev0verDrive, post:12, topic:156722”]

You’ll need a few new collision object channels for this. Here’s a few decent tutorials on climbing, vaulting etc.

Thank you!

Gratz on making something extremely simple incredibly complex.

“Jump” is not a thing. You cant rely on it since character can be forced to enter falling without using jump.

you should use the **On Character Movement Updated **event in order to check for changes.

The event is eventually called (changing from falling to walking). Detect that, and call in a custom event to decide what the fall was supposed to do based on the speed of the movement component.

This has the benefit of also being network replicated.

Before you ask “how do you detect the change”.

Store the value of the movement mode in a variable every time the event is called.
Before you store the value you can now compare the variable to the actual mode the event is changing to.
if your previous mode was falling, and the new mode is walking - you landed.