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?
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)
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?
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
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!
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?
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
That’s exactly what I want. I’m just ragdolling the player when he falls off. Thanks 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?
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
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 !
Hi ! would it be cool to add the fact that if you fall in the water you take less damage ?
Hi it’s the new version of the code :
--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--
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
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)
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.
[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!