You could connect the .Touched event for each part parented to the character(.Touched apparently works on terrain, although I think it fires when the player touches water also). You could also check the Humanoid’s FloorMaterial, and if it’s set to “Air” then the character isn’t touching the ground.
Then for damage, you could take damage based on HumanoidRootPart’s Y velocity(only damages the player when he falls), or HumanoidRootPart’s Velocity.Magnitude(which would allow damage from the player moving at high velocities in other directions).
You can utilize the Humanoid.StateChanged event to achieve your goal. Every time if your humanoid state changes to freefall, you can get the y position once it changed, then once the state changes to Landed, you can get the Y value of the HumanoidRootPart when the Humanoid lands, and take the difference of the two Y values. Depending on the difference, you can scale your damage accordingly.
What I would do is do a combination of detecting velocity of the y axis of the torso and GetPropertyChangedSignal for FloorMaterial on the Humanoid. If the floor material is changed from air to some material and the velocity of the torso was some value then you could say they fell from a certain height.
You would then have to run something every frame(this wouldn’t be too bad, because there isn’t intense computing power going on):
1A. Check if player is off the ground via raycasting
1B. If player is not off the ground, repeat 1A until they are off the ground
2. Get the velocity of the player, whilst raycasting downwards equal to the velocity(so if the velocity is "0, 5, 0", you would rayacst "5" studs downwards)
3A. If it hits a part, wait for the player to hit the ground, then take corresponding damage
3B. If not, keep doing #2 until 3A is met
I made a fall damage script. It worked every time I tested it, and doesn’t damage a player when he falls into the water.
--height at which the player starts to take damage
local minimumDamageHeight = 20
--height at which the player takes maximumDamage damage
local maximumDamageHeight = 100
--if height exceeds maximumDamageHeight,
--player will take over maximumDamage damage
local maximumDamage = 100
local velocity
-- linear interpolation function
local function lerp(a,b,t)
return (1-t)*a+b*t
end
--inverse of lerp function
local function invLerp(a,b,v)
return (v-a)/(b-a)
end
--remaps value, used for max and min damage heights.
local function remap(iMin,iMax,oMin,oMax,v)
return lerp(oMin,oMax,invLerp(iMin,iMax,v))
end
-- fired when FloorMaterial changes
script.Parent.Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
--calculate fall height from velocity
local height = (velocity * velocity)/(2*workspace.Gravity)
--remap damage based on variables
local damage = remap(minimumDamageHeight,maximumDamageHeight,0,maximumDamage,height)
--prevent negative damage
damage = math.max(damage,0)
--apply damage
script.Parent.Humanoid:TakeDamage(damage)
end)
--fires before each physics update
game:GetService("RunService").Stepped:Connect(function(time,step)
--update velocity variable
velocity = script.Parent.HumanoidRootPart.Velocity.Y
end)
I put this in a LocalScript in StarterCharacterScripts.
It seems pretty easy for players to hack the client or the server(I’ve heard you can download programs to do it for you), so I don’t worry about it. In this case a client side script works better.
Probably the reason it doesn’t work in a server script is the .Stepped function. The server and client aren’t quite synced, so there might be a little delay(and so velocity is low by the time GetPropertyChangedSignal(“FloorMaterial”) gets fired.
The loop I use is called just before physics are calculated, so it is in sync. I tried getting the velocity only after an event was fired(tried multiple events), but it had already responded to collision and was set to 0. For fall damage, you need the velocity on the previous physics frame, which can’t be done with just events.
actually from what i have read, serverside scripts can’t be accessed by anything, unlike local scripts they cannot be decompiled by third party software. I will consult others here on devforum regarding this. Thank you either way
I made a script once that measured the highest value of the changing velocity property of a part. seems to work well. (Used it to measure projectile speeds on a physics catapult)
It assumes there is no slowing down then reaccelerating before impact.
If you had it run (or something similar) each freefall, it could work. Then you just apply damage base on max impact speed.
local rock = script.Parent
local maxVelocity = 0
local currentIsLarger = true
while currentIsLarger do
if maxVelocity <= rock.Velocity.Magnitude then
maxVelocity = rock.Velocity.Magnitude
else
currentIsLarger = false
end
wait()
end
print(maxVelocity)
If you haven’t gotten a solution to this yet, allow me to help;
State changed should work reliably, as I’ve used it before on my own fall damage scripts and I haven’t found any problems yet. You just have to first have a function saying when its changed (by using an if statement to check if the state is Enum.HumanoidStateType.Freefall). Then have another function inside the previous one saying when it’s changed back to Enum.HumanoidStateType.Landed.
Example:
game.Players.PlayerAdded:Connect(function(Player)
Player.CharacterAdded:Connect(function()
local StateChangedFunction
StateChangedFunction = Player.Character.Humanoid.StateChanged:Connect(function(OldState, NewState)
if NewState ~= Enum.HumanoidStateType.Jumping then
if NewState == Enum.HumanoidStateType.Freefall then
local LastHeight = Player.Character.HumanoidRootPart.Position.Y
local Function
Function = Player.Character.Humanoid.StateChanged:Connect(function(OldState2, NewState2)
if OldState2 == Enum.HumanoidStateType.Freefall then
Player.Character.Humanoid:TakeDamage(math.abs(LastHeight - Player.Character.HumanoidRootPart.Position.Y))
wait()
if Function then
Function:Disconnect()
end
else
if Function then
Function:Disconnect()
end
end
end)
end
end
end)
Player.Character.Humanoid.Died:Connect(function()
if StateChangedFunction then
StateChangedFunction:Disconnect()
end
end)
end)
end)
Please note that this script is purely for an example, and is not recommended for an actual game due to it damaging the player whenever they slightly fall. (edits should be made)
(This is also a server script inside ServerScriptService)