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 think comparing start and end heights would cause problems. Isn’t that what he’s doing already?
I’ve never needed to set a specific height for fall damage, but if you need to, you could calculate height from velocity with a little math:
local height = (velocity*velocity)/(2*workspace.Gravity)
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.
while this works great, what worries me is that it’s a local script, wouldn’t this make it vulnerable to client side manipulation?
Its not even a localscript, you put it in a CharacterAdded function.
you just can’t depend on loops, they will not always be in sync with physics/movement.
your approach needs to be event based. (using custom wrapped events or provided events, doesn’t matter)
what do you mean? i don’t get it, it IS a local script
I tried to transfer it to a severscript inside StarterCharacterScripts, however even tho there are no errors it doesn’t work
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.
Here’s an article about roblox and hacking:
yes you might be right, i tried using heartbeat instead but still velocity is around 0 when the player lands
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)
How tho? Would it allow for migration of his script server side? I am really reluctant to use a local script for damage
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)
raycast below the character with runservice loop, and if the instance’s material changes from nil
to Enum.Material.IDK
then fire an event declaring that the player has landed
Why not just do this inside of a character? This solution is pretty simple and very reliable. May be exploitable but I’m not one of those people that cry about exploits that prevent such a small thing such as fall damage, just ban them, or don’t even do anything.
This can happen when jumping but you can use ways to prevent that.
--// Variables
local Character = script.Parent
local Humanoid = script.Parent:WaitForChild("Humanoid")
local Root = script.Parent:WaitForChild("Torso",math.huge)
local BuiltupMagnitude = 0
local FallingStates = {Enum.HumanoidStateType.Freefall}
Humanoid.StateChanged:Connect(function(old,new)
print("Current State : "..tostring(new))
if table.find(FallingStates,new) and not table.find(FallingStates,old) then
repeat
local Magnitude = Root.Velocity.Magnitude
if Magnitude>BuiltupMagnitude then
BuiltupMagnitude = Magnitude
end
task.wait()
until not table.find(FallingStates,Humanoid:GetState())
if BuiltupMagnitude>=30 then
Humanoid.Health-=BuiltupMagnitude/2
BuiltupMagnitude = 0
end
end
end)