Best way to make fall damage?

As many of you probably think, fall damage is a nice addition to your game. I think the same. Though, no matter which method I tried in making the script, nothing seemed to be reliable, everything I wrote did indeed work, it just wasn’t great. Making fall damage using the wait() method is the most unreliable method, what if the distance is much greater than the time it took you to fall down? what if you were intentionally floating? I have also tried another method calculating the distance between the position where you started freefalling and stopped freefalling. But “freefalling” means being “in air”, so technically I start falling when I just jump, and not when I actually start falling down. So, if I were to jump really high and fall down to the same spot or just above the spot I jumped from, I would take no damage even though I have spent at least 15 seconds falling down.
Any thoughts, fine people of the devforum?

9 Likes

I’ve seen a whole lot of ways to do fall damage and they normally never look great. A bit of time ago though, so recently but not too recently, I was taught a good way of doing fall damage and I think it sounds pretty logical as well. I’ve never applied it in practice before but maybe you could try.

So first off, we’ll need to be thinking only in terms of time. Wait is bad because it yields the thread so we’re losing out on accuracy and arbitrary timing. Floating is a non-problem, players might use it to cheat on games where the ground is a critical part of gameplay so they can still be killed to prevent them from doing that. Calculating distance isn’t needed, that can be covered by time.

You basically have it down already, but you’ll need to change your method up. Track the time between when a player enters the Freefalling state and the Landed state. Determine a threshold for damage, such as 5 seconds before they’re eligible for damage. Take the time they spent falling, remove the threshold from it and then apply your damage according to this value in a rate-like manner (e.g. for every extra second spent falling, 10 damage).

This system can be wholly event-based because you’ll mainly be relying on the StateChanged event to check when a player enters or exits a state which will also catch brief states (usually only last a frame or some). Get the tick when the player enters freefalling or exits it, and compare.

For example…

local SECONDS_THRESHOLD = 5
local DAMAGE_PER_SECOND = 10

local lastFreefall

Humanoid.StateChanged:Connect(function (old, new)
    if new == Enum.HumanoidStateType.Freefalling then
        lastFreefall = tick()
    elseif old == Enum.HumanoidStateType.Freefalling and new == Enum.HumanoidStateType.Landed and lastFreefall then
        local currentTime = tick()
        if currentTime > lastFreefall + SECONDS_THRESHOLD then
            -- This may be the wrong calculation
            local refinedTime = currentTime - (lastFreefall + SECONDS_THRESHOLD)
            Humanoid:TakeDamage(refinedTime * DAMAGE_PER_SECOND)
        end
    end
end)
34 Likes

This seems practical. I will definitely try it myself. Though, this method seems to be a gimmick of its own kind. Is there, perhaps a way to actually track the velocity the player is falling down at, then dealing the fall damage based on your velocity?

3 Likes

I’ve never tried anything in regards to velocity, but I assume the best way to do something like that would be to check the HumanoidRootPart’s. Their Y velocity would, of course, determine the vertical units for their movement.

local fallVelocity = HumanoidRootPart.Velocity.Y
9 Likes

Hey! It seems I have actually found a solution, and its pretty easy and wild!

Apparently, when your state changes to “Landed”, your velocity is still the same for a brief moment, so, if I were to jump and print out my velocity when I land, it would print out 50. This is so neat and easy, thank you, Colbert!

36 Likes

Wow, this is nice. But, how will I check it in free-falling? I’m making a ragdoll system that should ragdoll the player when his HumanoidRootPart velocity’s Y is greater than 30. Is there a way to do it without using a while loop and looping through every player?

1 Like

I don’t know if this is close to the thing you want to do

Use Ray to measure the distance between the player and the Part below the player

Determine the distance at which you want to do what you want to do

Use the following

function onFreeFall()
Put what you want here Ray and Distance
end
Humanoid.FreeFalling:connect(onFreeFall)

Humanoid.StateChanged:Connect(function(oldState, newState)
Add the condition according to the change
end)

I am sorry if my writing is bad, I do not speak English

This is an example of what I talked about

7 Likes

That’s exactly what I want. I’m just ragdolling the player when he falls off. Thanks :smiley: but I still don’t know what to put in function onFreeFall(). Could you please show me what your code was? I’ll try to learn from it. And, why didn’t it happen when the character jumped on the red part?

2 Likes

I am sorry for the late reply

Determine the height the player would die from if they fell

This code when placed inside a Script in ServerScriptService

game:GetService('Players').PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
local root = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")
local animation	= script:WaitForChild("Animation")	
local animate = character:WaitForChild("Animate")		
local EndJump = 50 --Put the distance you want		
local FallDeath = false	
local heartbeat = nil		
		
		
-- Create the animation just the way you want it		
local function playAnimationFromServer(MyCharacter, MyAnimation)
	local MyHumanoid = MyCharacter:FindFirstChildOfClass("Humanoid")
	if MyHumanoid then
		local MyAnimator = MyHumanoid:FindFirstChildOfClass("Animator")
		if MyAnimator then
			local MyTrack = MyAnimator:LoadAnimation(MyAnimation)
			MyTrack:Play()
			return MyTrack
		end
	end
end			
		
		
local function onFallingDeath()
    local Base = root.CFrame * Vector3.new(0,-10,0)
    local ray = Ray.new(root.CFrame.p, (Base - root.CFrame.p).unit * 300)
	local part, position = workspace:FindPartOnRay(ray, character, false, true)
	local distance = (root.CFrame.p - position).magnitude		
	print(distance)		
	if distance > EndJump and FallDeath == false then	
        FallDeath = true		
		animate.Disabled = true		
		-- Animation playback	
		playAnimationFromServer(character, animation)	
		-- It makes the downward movement slow		
		local bodyvelocity = Instance.new("BodyVelocity")
        bodyvelocity.maxForce = Vector3.new(0, 99999, 0)
	    bodyvelocity.P = 1250
	    bodyvelocity.velocity = Vector3.new(0,-10,0)
	    bodyvelocity.Parent = root		
		-- He cannot move while he is falling	
		humanoid.WalkSpeed = 0		
	end			
end	
		
		
humanoid.StateChanged:Connect(function(oldState, newState)
	if newState ~= Enum.HumanoidStateType.Freefall then		
		if FallDeath == true then	
		    FallDeath = false		
            for _, playingTracks in pairs(humanoid:GetPlayingAnimationTracks()) do		
			   playingTracks:Stop()
	        end		
			local MyBodyVelocity = root:FindFirstChild("BodyVelocity")
		    if MyBodyVelocity then
		       MyBodyVelocity:Destroy()
		    end
			animate.Disabled = false		
			humanoid.Health = 0		
		end			
	end
end)
		

humanoid.FreeFalling:connect(function(onFreeFall)
    if onFreeFall == true then
	   heartbeat = game:GetService("RunService").Heartbeat:Connect(onFallingDeath)
	end			
	if onFreeFall == false then	
	   heartbeat:Disconnect()		
	   heartbeat = nil		
	end			
end)
		
		
end)
end)

If it doesn’t work well tell me about the problem

I wish you good luck in what you want to do

10 Likes

Hey !
I have found a new way for the fall dammage (maybe already found) !
when you land, it subtracts the value from the height you landed at height you started falling !
Put this into PlayerCharacterScripts !
Sorry for my bad english …

--variable--

local Player = game.Players.LocalPlayer
local Character = Player.Character
local Humanoid = Character:WaitForChild("Humanoid")
local Y
local YF
local YL

--variable--

--SetVariable/ApplyDamage--

Humanoid.StateChanged:Connect(function (oldState, newState)
	if newState == Enum.HumanoidStateType.Freefall then
		YF = Y
	end
end)

Humanoid.StateChanged:Connect(function (oldState, newState)
	if newState == Enum.HumanoidStateType.Landed then
		YL = Y
		if YF - YL > 30 then --MinEightForTakeDamage--
			Humanoid:TakeDamage(YF-YL) --ApplyDamage--
		end
	end
end)

----SetVariable/ApplyDamage--

--GetY--

while true do
	Y = Character:WaitForChild("HumanoidRootPart").Position.Y
	wait()
end

--GetY--

Have a good day ! :grin:

3 Likes

Hi ! would it be cool to add the fact that if you fall in the water you take less damage ? :thinking:

Hi it’s the new version of the code :wink: :

--Variable--

local Player = game.Players.LocalPlayer
local Character = Player.Character
local Humanoid = Character:WaitForChild("Humanoid")
local RootPart = Character:WaitForChild("HumanoidRootPart")
local RunService = game:GetService("RunService")
local TouchWater = false
local Y
local YF
local YL

--Variable--

--CheckIfThePlayerIsInWater--

RunService.Heartbeat:Connect(function()
	local BodyPos = Player.Character.HumanoidRootPart.CFrame
	local x, y, z = BodyPos.X, BodyPos.Y, BodyPos.Z

	local min, max = Vector3.new(x + 0.01, y + 0.01, z + 0.01), Vector3.new(x - 0.01, y - 0.01, z - 0.01)
	local region = Region3.new(max, min)
	region = region:ExpandToGrid(4)

	if region then
		local material = workspace.Terrain:ReadVoxels(region, 4)
		if material[1][1][1] == Enum.Material.Water then
			TouchWater = true
		else
			TouchWater = false
		end
	end
end)

--CheckIfThePlayerIsInWater--

--SetVariable/ApplyDamage--

Humanoid.StateChanged:Connect(function (oldState, newState)
	if newState == Enum.HumanoidStateType.Freefall then
		YF = Y
	end
end)

Humanoid.StateChanged:Connect(function (oldState, newState)
	if newState == Enum.HumanoidStateType.Landed then
		YL = Y
		if YF - YL > 100 and TouchWater then --MinEightForTakeDamageIfThePlayerTouchWater--
			Humanoid:TakeDamage(100) --ApplyDamage--
		else
			if YF - YL > 30 then  --MinEightForTakeDamage--
				Humanoid:TakeDamage(YF-YL) --ApplyDamage--
				print(YF-YL)
			end
		end
	end
end)

----SetVariable/ApplyDamage--

--GetY--

while true do
	Y = Character:WaitForChild("HumanoidRootPart").Position.Y
	wait()
end

--GetY--
7 Likes

Sorry for the bump but I got some additional information.

The use of the Humanoid may not apply to everyone, so I’m posting this solution!
It doesn’t use Humanoid or States at all, but it works well :slight_smile:
This is good if you have custom rigs, NPCs, etc etc or just don’t wanna use Humanoid states at all (why I made it)!

This works on the client and server.

This script was made in a LocalScript in the Character (tested with a Script & works too) but you can modify it as necessary. The THRESHOLD constant is the minimum velocity change before we detect a “fall” so it’s heavily customizable. I recommend not having the threshold at or around your Character’s JumpPower, otherwise everytime they jump it’ll detect a fall.

Here ya go <3

local THRESHOLD = 83

local RunService = game:GetService("RunService")

local Character = script.Parent
local RootPart = Character:WaitForChild("HumanoidRootPart")

local function calculateDamageFromVelocity(velocity)
	return math.abs(velocity ^ 3) / 100000
end

local function damageHumanoid(distance)
	print("Damage:", calculateDamageFromVelocity(distance), "Distance:", distance)
	-- TODO: Character.Humanoid:TakeDamage(calculateDamageFromVelocity(distance))
end

local lastMajorVelocity = 0
RunService.Heartbeat:Connect(function(dt)
	local newVelocityY = math.abs(RootPart.AssemblyLinearVelocity.Y)
	
	local distanceY = math.abs(newVelocityY - lastMajorVelocity)
	if newVelocityY <= 5 and lastMajorVelocity >= THRESHOLD then
		damageHumanoid(distanceY)
		lastMajorVelocity = newVelocityY
	end

	if distanceY <= 5 then
		lastMajorVelocity = newVelocityY
	end
end)
13 Likes

Thanks for this! This is the underlying thing for the fall damage script I ended up making. You can check it out here if you still haven’t figured it out (unlikely though).

Would’ve never done it without this. :slight_smile:

[Edit: Did not realise I bumped the thread, sorry about that.]

But “freefalling” means being “in air”, so technically I start falling when I just jump, and not when I actually start falling down.

well, you can check if the players Y position is going down. I did that and it works just fine
(sorry for replying 4 years later i just thought you might like to know if you didnt figure that out already)

You can do this in a much simpler way by just checking if the HumanoidRootPart’s AssemblyLinearVelocity is less than 0!

you are indeed right on this, i had not thought about that until a few days after i posted that lol!