Fall Damage Script

Information


After around 2 days of work, including a DevForum post (that I found the solution to myself lol), I am releasing my Fall Damage script!

As you can see here in this video, you take damage when falling from at least 75 studs. (This can be changed to your likings in the script)

If you just want to add the script right away to your game, here is the link to the model:

Here is a game to test this in:

However, I don’t recommend doing this without reading the rest of this post.


Main Script Breakdown


This script has 2 other scripts inside it too. There are 2 LocalScripts, and a ModuleScript.

First, lets talk about the main script, and how it works.

local Character

repeat task.wait() until script.Parent:FindFirstChild("Humanoid")

Character = script.Parent

local Humanoid = Character:WaitForChild("Humanoid")
local HRP = Character:WaitForChild("HumanoidRootPart")
local Values = require(script.Values)

local function CalculateDamage(Magnitude)
	-- Calculates the amount of damage the player will take from the fall.
	local afterminus100;
	local totaldamage;
	afterminus100 = Magnitude - Values.MinimumFallDamageStuds
	if afterminus100 >= 0 then
		totaldamage = Magnitude*Values.DamageMultiplication + 10
	else
		totaldamage = 0
	end
	
	return totaldamage
end

Humanoid.StateChanged:Connect(function(oldstate,newstate)
	local magnitude;
	local total;
	if newstate == Enum.HumanoidStateType.Landed then -- Tells when the player landed
		--print('landed')
		Values.DontUpdateYet = true;
		magnitude = (Values.LastStandingPos - HRP.Position).magnitude -- measures the y position of where they fell from, and the Y position they have after they touched the ground.
		
		total = CalculateDamage(magnitude)
		
		
		-- Here, the script checks if the total is greater than 0. If it is, then it damages the player and plays the sound.
		if total > 0 then
			Humanoid.Health = Humanoid.Health - total
			script.DamageSound:Play()
		end
		
		Values.DontUpdateYet = false;
	end
end)

The script starts off by doing a repeat loop until the Character is found.
The script then defines 3 variables, including the ModuleScript that will be talked about later in this post.

After those 3 variables are defined, a function named CalculateDamage is defined. A parameter is passed through when calling this function, which is the Magnitude.

Before I continue, for anyone unaware of what Magnitude is, it is the length of a vector.

You can read more about it here:

Now, lets get back to the script.
2 variables in this function are defined:

  1. afterminus100 (don’t mind the name, the minimum studs when I began working on this script 2 days ago was going to be 100 studs, and I’m too lazy to fix it, so just don’t mind the name of this variable.)

  2. totaldamage

afterminus100 will be used to determine how many extra studs they fell above 75 (or your number of studs).
totaldamage will be used later for the total amount of damage to do to the player’s Humanoid.

On the next line (line 16), a value is set for afterminus100. The value is set by the Magnitude subtracted by the Minimum amount of studs you set to take fall damage in the Values ModuleScript.

After this, the script checks if afterminus100’s value is greater or equal to 0. If it isn’t, totaldamage is set to 0.

If it is however, totaldamage’s value is set to the Magnitude’s value * the value you set for the damage multiplication in the Values ModuleScript. I set it at 0.05, however you can change it to whatever you would like it to be.

return totaldamage

After all of this, the totaldamage variable is returned, so whenever this function is called, it returns the total amount of damage the player will take from the fall.

Next up is detecting when the state of the Humanoid is changed.

This is done by using Humanoid.StateChanged.

	local magnitude;
	local total;
	if newstate == Enum.HumanoidStateType.Landed then -- Tells when the player landed
		--print('landed')
		Values.DontUpdateYet = true;
		magnitude = (Values.LastStandingPos - HRP.Position).magnitude -- measures the y position of where they fell from, and the Y position they have after they touched the ground.
		
		total = CalculateDamage(magnitude)
		
		
		-- Here, the script checks if the total is greater than 0. If it is, then it damages the player and plays the sound.
		if total > 0 then
			Humanoid.Health = Humanoid.Health - total
			script.DamageSound:Play()
		end
		
		Values.DontUpdateYet = false;
	end
end)

This is what we will be looking at now.

After Humanoid.StateChanged is connected to a function, we use the 2 parameters:

  • oldstate
  • newstate

These paramets easily allow us to identify the previous state of the Humanoid, and not go through a nightmare doing it. Thanks Roblox for once :slight_smile:

Anyways, this function starts by defining 2 variables.

  • magnitude
  • total

The script then checks the value of the newstate parameter:

if newstate == Enum.HumanoidStateType.Landed then -- Tells when the player landed

If it equals

Enum.HumanoidStateType.Landed

Then, the script will know the player just landed, as this state lasts only for a brief amount of time as can be seen here:

A variable in the Values ModuleScript named “DontUpdateYet” has been set to false. What is this variable, and what will it do?

Well, as will be seen when we get into the second script, it will tell if it the LastStandingPos is able to be updated yet.

Why does this need to exist?
Because the LastStandingPos will update right before the fall CalculateDamage function runs, therefore the Magnitude that will be passed through will be like 0 or something, and that’s not what we want.

Next line, we get back to that HRP variable.
I usually type HRP instead of HumanoidRootPart in my scripts, as it is much easier to type and faster.

local lastHeight = Values.LastStandingPos.Y
local currentHeight = HRP.Position.Y
magnitude = lastHeight - currentHeight -- measures the y position of where they fell from, and the Y position they have after they touched the ground.

This is the line that calculates the magnitude. Nothing to mention here that I know of.

total = CalculateDamage(magnitude)

The total damage that the player will take is then calculated using the CalculateDamage function.

if total > 0 then
	Humanoid.Health = Humanoid.Health - total
	script.DamageSound:Play()
end
		
Values.DontUpdateYet = false;

The script then checks if the total is over 0. If it is, then the player takes the fall damage and then plays the fall damage sound. I will mention this sound again later in this post.

Anyways, at this point you should understand what is going on for the rest of this code snippet.

That is the end of this script.


Second Script Breakdown


This script is more straight forward, unlike the last one. I’m not going to explain anything here, however there are some comments in the script.


-- The script needs this to check the state to not update the Last Standing Position Variable.
local function getHumanoidState(h)
	return h:GetState()
end

-- The states where the Last Standing Y Position variable will not be changed.
local States = {
	[Enum.HumanoidStateType.Running] = true;
	[Enum.HumanoidStateType.RunningNoPhysics] = true;
	[Enum.HumanoidStateType.Climbing] = true;
	[Enum.HumanoidStateType.Ragdoll] = true;
	[Enum.HumanoidStateType.FallingDown] = true;
	[Enum.HumanoidStateType.Flying] = true;
	[Enum.HumanoidStateType.GettingUp] = true;
	[Enum.HumanoidStateType.Dead] = true;
	[Enum.HumanoidStateType.None] = true;
	[Enum.HumanoidStateType.Physics] = true;
	[Enum.HumanoidStateType.Swimming] = true;
	[Enum.HumanoidStateType.Seated] = true;
	[Enum.HumanoidStateType.PlatformStanding] = true;
}

local Character

repeat task.wait() until script.Parent.Parent:FindFirstChild("Humanoid")

Character = script.Parent.Parent

local Humanoid = Character:WaitForChild("Humanoid")
local HRP = Character:WaitForChild("HumanoidRootPart")
local Values = require(script.Parent.Values)

while true do
	local state = getHumanoidState(Humanoid)
	--print(state)
	if States[state] and not Values.DontUpdateYet then
		Values.LastStandingPos = HRP.Position
		--print(Values.LastStandingPos)
	end
	task.wait()
end

ModuleScript Breakdown


local module = {}

module.LastStandingPos = nil;
module.DontUpdateYet = false;
module.DamageMultiplication = 0.05
module.MinimumFallDamageStuds = 75

return module

This script is extremely straight forward, it simply stores the values that were explained earlier.


Fall Damage Sound


Due to Roblox’s recent audio privacy update, it is not possible for anyone to use the same ID, and as of now Roblox does not allow making audios public. (Thanks a lot Roblox)

Until Roblox allows people to make their audios public, the only way for me to share this audio with you is to put the file for it in this post, and for you to upload it yourself.

Here is the audio file:

Obviously, you can change it to your own sound, but if you want the same one as in the video shown at the beginning of this post, this is your only way of getting it.


*How to add this script to your game


Here is a model of the script:

Simply take the model, import it in from Toolbox, and put it in StarterPlayer → StarterCharacterScripts.

You can then change the settings I mentioned earlier in the Values Module.


Anything else?


I don’t have anything else to say. Enjoy the script!

34 Likes

this is cool i will use this :sunglasses:

1 Like

Ty, this post took me around 30m-1h to make too btw

1 Like

Thanks for this amazing resource. I and a ton of other people on this forum will definitely find this useful. Thanks for taking time out of your day to put in a ton of effort for this. Keep it up!

4 Likes

Great job you did there, appreciate your effort, I would definitely use it when I need,

I am sure it will also help beginners and people who practice programming and want to taste some fresh and good codes to help them understand more. Good job :+1:

5 Likes

Hey there. Nice post - really liked how you explained everything in excruciating detail :laughing:.

Just a small thing: while yes, the checking for humanoid state changes works and it’s a fairly simple way of making this work, have you considered using raycasting for this?

Humanoid state changes can be manipulated by exploiters (very easily actually), so an anti-fall damage script for this would be dead simple to make hint: bind Humanoid:ChangeState(Enum.HumanoidStateType.Running) to RenderStepped :wink:

With raycasting, you could handle everything related to fall damage on the server, so it would be a lot harder to make an anti-fall script.
(Also - Make sure to raycast from the head or something, not the torso or HRP, as those can also be removed by exploiters. Removing the head would be a lot harder as it involves permadeath which can be easily detected.)

3 Likes

I have never considered using raycasting for this.

That line you made simply wouldn’t allow the player to jump, as they will always be put in the running state.

Exploiters can delete the script too, making it easier.

I have also been working on a server sided detection if the script has been deleted or not for this.

2 Likes

The line I wrote was an example of how to bypass your antifall, sorry for not clarifying!

Also, detecting if a script has been deleted or not from the server side is impossible. You can blame FE for that. (although maybe if there was a thing where changes on the clientside would be visible to the server side but just not replicated? that would be kind of helpful)

Also, if you still don’t feel like using raycasting (for whatever odd reason you may have) - the FreeFalling event on the humanoid exists, and I don’t think it’s influenced by state changing (I’ll have to test this later)

1 Like

You’re wrong. I’ve done it before.

Also, I don’t feel like using raycasting as it is way more complicated.

1 Like

you used wait
use task.wait to make your code 9999999 times better

2 Likes

well, raycasting is more accurate

2 Likes

Good for you man. A lot of people will use this. I probably won’t because my game is a close range fps but for those who do, credit this guy. People like this put in a lot of hard work with 0 pay back.

1 Like

Yeah I forgot, just fixed it rn

1 Like

I mean I’ve been thinking about making a brutal obby with fall damage in it, using this script

1 Like

Curious on how you did it.

1 Like

This is a little bit of a misconception; while task.wait is certainly far more accurate, it doesn’t necessarily mean it’ll make code so much better.

Ultimately, how good your code is depends on a lot of factors, primarily efficiency, readability, maintainability, robustness, etc, etc. Just using task.wait won’t really do much unless you have time critical code that has to be down to sub-frame timing.

4 Likes

That’d be an interesting thread to make. I’m very certain many developers would make use of it if you showed us how.

Really?

local RayParams = RaycastParams.new()
RayParams.FilterType = Enum.RaycastFilterType.Blacklist
RayParams.FilterDescendantInstances = {Character}
game:GetService("RunService").Heartbeat:Connect(function()
    local RayResult = workspace:Raycast(
        Head.Position - Vector3.new(0,5,0), -- Origin - Raycast from 5 studs down 
                                            -- so you get position of feet
        Head.Position - Vector3.new(0,5005,0), -- Direction - Where you want to raycast to
        RayParams
    )
    
    if RayResult then
        local Distance = RayResult.Distance

        if Distance > 50 then
            -- I don't know, you're the person who said it was more complicated
            -- Simple base code for you to start writing your raycasting code
        end
    else
        -- Raycast ended up being longer than 5000 studs, assume it's lethal lol
    end
end)

Does this script do the classic Natural Disaster Survival and count movement from glitchy physics parts under you as falling down?

No. it should not do whatever that is.

Please don’t use this fall damage script, there is a huge oversight. If you fall while moving forward it counts that forward movement as falling down and will damage you with even the smallest of falls if you are moving quickly.

It will also damage you if you jump up onto a taller structure, if it’s high enough.

Luckily this can be easily fixed by only considering the Y change in positions, and allowing this value to be negative:

local lastHeight = Values.LastStandingPos.Y
local currentHeight = HRP.Position.Y
magnitude = lastHeight - currentHeight -- measures the y position of where they fell from, and the Y position they have after they touched the ground.

And yes you could make this one line, but so its easier to understand I made it three.

1 Like