Detecting if a player is underwater (terrain)

I’m trying to make a custom swimming system but I’m running into some issues when it comes to detecting if a player is in water. As a heads up, I do not believe I can use the default swimming states because they don’t seem to be usable without the default swimming mechanics (I’ve tried countering their effects but it just didn’t work well.)
https://streamable.com/br4qfa

I’m currently shooting a ray from my old position to my new one, seeing if there is water in the way, and if there is I start swimming. I then do this process backwards to detect if a player has left water (ray shoots from new position to old position to try to find water). The problem occurs when the water is next to another type of terrain-- I’ve tried using smart rays that try again if they run into the wrong type of material, thus ignoring the previous material, but it seems like if two different types of terrain are right next to eachother, the ray doesn’t work.
I.e only the surface of the cobblestone is present, not the surface of the water.

I’ve tried using region3 and reading from voxels but as far as I can tell this is just simply too imprecise. You randomly start swimming when you’re besides water, and randomly stop swimming while in water. Unless I am doing it wrong.

Any ideas?

1 Like

I’m no programmer but since water is always at the same altitude (sea level), can’t you just check if the user is below a certain height?

I don’t know how your map is designed though so this may or may not work depending on map design.

Hope that helped.

Water may vary in height on roblox, as it can be painted like any other terrain.
image

You can do something where the height changes based on “region”, I don’t know if what I’m suggesting is a more complex solution than what someone else may suggest, but you can have it so that if a player is in a certain area, the height of the water changes based on how it’s painted on.

Can’t you just access the player’s animations and change it to whatever you want?

I’m looking to detect when you are in the water & when you leave the water, nothing about animations.

1 Like

Seems needlessly complicated. I know some games who do this for small things, but something like having zones specifically blotted out for every single place in the game is like adding an extra layer to map design that really shouldn’t need to exist.

image
This is what I mean. Can’t you change the animation ID to whatever you want?

Again, what do animations have to do with anything and how would they help solve anything?

Oops, I thought what you were looking for is custom swimming animation. A little misunderstanding. However, you can set up an event to detect when an animation plays to enable that system?

No, because

I.e the default state changes that the default animation script relies on are completely disabled outright to stop the default roblox swim physics from kicking in.
(Besides there’s no need to read an animation to tell what a player is doing-- the animation script already provides the code that finds if you’re swimming, which again, is by using states)

Maybe you create an invisible uncollidible anchored part around each region, and detect whenever the player enters one?

Already basically addressed this exact suggestion

You should refer to this property. Humanoid | Roblox Creator Documentation

1 Like

FloorMaterial is only for the floor. It reads as “air” when you’re floating in water.

Edit: Nor could you be stood on it, not even for a single frame.

1 Like

Did you ever find a way to solve this?

If I did I don’t remember what the solution was anymore. Probably something janky like teleporting an invisible part over the player every frame and checking if this part’s velocity started increasing on the Y axis from buoyancy

I have this piece of code that checks if the players camera is underwater, and then applies an affect to it. You could probably easily adapt it to checking for the humanoid root part. @Kizylle

local function _underwaterCheck() : nil
    local offset : number = 0.01
    local cameraPosition : Vector3
    local camera : Camera = workspace.CurrentCamera
    local region : Region3?

    task.spawn(function()
        while task.wait(0.2) do
            cameraPosition = camera.CFrame.Position
            region = Region3.new(Vector3.new(cameraPosition.X - offset, cameraPosition.Y - offset, cameraPosition.Z - offset), Vector3.new(cameraPosition.X + offset, cameraPosition.Y + offset, cameraPosition.Z + offset)):ExpandToGrid(4)
        
            if region then
                local material = workspace.Terrain:ReadVoxels(region, 4)
                if material[1][1][1] == Enum.Material.Water then
                    --YOUR CAMERA IS UNDER WATER
                    print("underwater")
                else
                    --YOUR CAMERA IS NOT UNDER WATER
                    print("on land")
                end
            end
        end
    end)
end

(It checks the actual water material)

5 Likes