New: Nitro Boost our Discord server and receive full donation perks here on the website! Join the Scripting Helpers Discord Server to learn more! You can also Support on Patreon as always.
Still have questions? Join our Discord server and get real time help.
Log in to vote

How do I detect if a player lands in water?

Asked by 11 months ago

So here's the deal. I am trying to make a diving simulator game. However, I don't know how to make the diving work. I have found it challenging to program a way that scans when the player falls then hits the water, and other ways to do it. I have tried connecting with scripts using RemoteEvents, I have tried doing it all through local script, I have tried doing it all through normal script.

Put a transparent, a Non-Collision Object/Barrier over the water or in it, and when the player touches it makes the script print something that lets you know that the water has been dived into, or set a value of the player to something... Etc. JayShepherdMD 128 — 11mo
What did you try? hiimgoodpack 2009 — 11mo

2 answers

Log in to vote
Answered by
M39a9am3R 3209 Moderation Voter Community Moderator
11 months ago
Edited 11 months ago



Since Scripting Helpers is made to help people who have issues with their script or get people going in the right direction on projects, I can not give you exact code for your project. I am able to give you some examples and resources to look into.

Location and Setup

First off, you're going to want to think about where your script is going to go. Since you're tracking changes of Humanoid states and we'll be discussing RunService events, we'll want to pick out a LocalScript.

With local scripts, you're going to want to keep those within the player. Since we'll be messing with the Character quite a bit with state changes we'll want to have the script load with the Character. In the StarterPlayer service, you can place the LocalScript in StarterCharacterScripts.


Now that the LocalScript has been placed in the appropriate location, we can grab the Humanoid from the script's parent. This way, whenever a new character loads, the script can start listening for the changes to the Humanoid's state. Since the Humanoid object might take a moment to load, I would recommend using a WaitForChild yield on it. Once the Humanoid is loaded, we can connect directly to the StateChanged event. The StateChanged event passes two parameters to our function, the old state of the humanoid and the new state of the humanoid.

script.Parent:WaitForChild("Humanoid",5).StateChanged:Connect(function(old, new)
    print(old.Name .. "\t\t" .. new.Name)

From here, we're given the following results in the output.

  • RunningNoPhysics Running spawning
  • Running Freefall spawning
  • Freefall Landed spawning
  • Landed Running spawning
  • Running RunningNoPhysics walking
  • RunningNoPhysics Jumping jumping
  • Jumping Freefall jumping and falling
  • Freefall Swimming hitting water

From that, we can determine when the jump had begun, the player is actively falling through the air, and when the user hits the water. You can send information to the server stating that the jump and splash have been completed.

Server Validation

For every project, big or small, the most necessary part for a game is to have server validation. You can send information from the client to the server, but the server should also be doing checks to indicate if that action was actually allowed. The general rule is to never trust the client as false information may be passed by the client (a potential exploiter) to the server. You'll always want the server to check in some way that the user had enough money for a tool or has a sight on a target.

Advanced Information

If you understand the concept of server validation, then this advanced information may be suitable for you. You can set up a loop to a Heatbeat event fire and, while the player is falling through the air, gather how fast it is falling. This can be achieved by using the Character's Torso or UpperTorso, and getting the Velocity.Y value with every "Heartbeat".

With events, you can set them up to wait. This means, instead of setting up the event to a function, you can have it as a wait in a while loop. For example, we can say...

while game:GetService("RunService").Heartbeat:Wait() do
    --Logic Here

Every time the event happens (which is based off the client's frame rates), then whatever is inside the loop will run. From this we can continually see what the Torso Velocity is. Personally, I prefer the RenderStepped as it is faster than Heartbeat; however, the Roblox Developer pages recommend Heartbeat so morally I must go by that.

Using the code below, I was able to determine the highest and lowest velocity of a Torso from a 50-jump sample on default settings with my own character. The fastest fall was at -55.727523803711 studs per second and the fastest jump was at 52.182495117188 studs per second.

wasMin = 0
wasMax = 0
function getMinMax()
    print(wasMin .. "\t" .. wasMax)
while game:GetService("RunService").RenderStepped:Wait() do
    local y = game.Players.LocalPlayer.Character.UpperTorso.Velocity.Y 
    if y ~= 0 then

Different samples and different models may produce varying results. Make sure your scripts can accurately determine a player's jump speeds.

Hopefully this answer puts you on the right track. If it does, don't forget to hit that accept answer button. If you have any questions or concerns, feel free to comment them below!
you know that when you pass a tuple to print, the args are separated with a tab already, why reinvent the wheel User#24403 26 — 11mo
I actually was not aware. I've worked with Python and Java so much that I didn't realize Lua was so kind about that. Thank you for the advice. M39a9am3R 3209 — 11mo
Log in to vote
Answered by 11 months ago

    local hum = plr.Character.Humanoid
    if (hum.HumanoidStateType == Enum.HumanoidStateType.Swimming) then

        print('Player is swimming!')


That only runs once. Thus, this answer is also wrong. DeceptiveCaster 2794 — 11mo
lmao this is so terribly wrong Mirzadaswag 110 — 11mo
Sorry. Cvllapse 30 — 11mo

Answer this question