Detecting If User Is In Water

I’m trying to detect if a user is in water, and I’ve tried following a few posts so far and I’m getting somewhere, but it’s not working for some reason. The hit point is clearly in water, and it’s returning air for some reason.

Edit: To clarify, I’m looking for what I did wrong not ways to do this. It’s a simple check for an anti-exploit, I’m not making a whole game out of this.

Code
local ray = Ray.new(Torso.Position, Vector3.new(0, 0, 0))
local a,hitpos,b,material = workspace:FindPartOnRay(ray, char)
local Part = Instance.new("Part")

Part.Size = Vector3.new(1,1,1)
Part.Anchored = true
Part.Position = hitpos
Part.Name = "THEWATERHIT"
Part.Parent = workspace

print(material, hitpos, a, b)

This is the output: Enum.Material.Air -49720.0508, 8.57024956, -50273.7031 nil 0, 0, 0

image

6 Likes

There is some ways that come to mind you can check if Humanoid State is Swimming or you can place an invisable block under water and check for collisions.

The reason I want to see if they’re in water requires me to not rely on the Swimming state, I want to see if they’re ACTUALLY in water.

1 Like

I think your best bet would be to place an invisible block in the water, turn off CanCollide and check for .Touched and a humanoid, as ArslanBakyl said.

1 Like

You could also try to use game.Workspace.Terrain.Touched:Connect() for some reason it only detected water collisions for me you can give it a try.

1 Like

This is just an idea, but maybe you could create a region as big as maybe the players characters bounding box, then use either RenderStepped or a loop depending if you want to run on the server or client, then set the CFrame of the region to the character. Then you can read the voxels of that region and determine if you get water returned?

If any of what I mentioned is bad practice or not properly used, then please correct me so I can learn from the mistake.

1 Like

If all of the above doesn’t work I am sure this video will help you.

1 Like

I’m basically doing that, but it keeps returning air instead of water. That’s what this whole post is about.

1 Like

Well yeah, that function is getting the materials and water is a terrain…

1 Like

I actually did something like this today, but the system was to either detect if someone was jumping on top of the water or was inside the water, it was quite easy (after finding out that using rays wouldn’t be that good compared to using regions).

The whole idea is to use Region3 and workspace.Terrain:ReadVoxels with the Head’s position (well region).
If the player is on top of water or inside water in general then their floor material will be Air, so a way to filter out the players that are not in water without using Regions would be checking their floor material type (humanoid.FloorMaterial), then you can do the following :

	local min = head.Position - (.5 * head.Size)
	local max = head.Position + (.5 * head.Size)	
	local region = Region3.new(min,max):ExpandToGrid(4)
	
	local material = workspace.Terrain:ReadVoxels(region,4)[1][1][1] 
	if material == Enum.Material.Water then
              print("under water")
        end

That’s how you’d check if the player is swimming or under water, however if you want to check if the user is on top of water or is in water in general then you can use their humanoidrootpart.

		min = root.Position - (4 * root.Size)
		max = root.Position + (4 * root.Size)	
		region = Region3.new(min,max):ExpandToGrid(4)
		material = workspace.Terrain:ReadVoxels(region,4)[1][1][1]

again if material is water then they’re under water or on top of water.

Using rays is horrible in this case because if you’re raycasting from inside the water it will not hit it, you can fix that by raycast from above the water (which you wouldn’t really know, and you’d never know if the ray won’t hit someone else’s character), that can be fixed by using FindPartOnRayWithWhitelist but at this point It’s just better to use regions, it’ll fit better.

32 Likes

You can raycast from the player’s humanoid root part downwards (and add the player’s character to the ignore list), and if it hits an object, you can get it’s material. From there you can use an if / else statement to check if it’s water or not.

This solution works most of the time, but for some reason when I jump in and fall back in the water it says I’m not in the water. :thinking: I’ll try to adapt it to work.

Of course it won’t say that, when you jump outside the water you’re not inside the water.
For “as soon as i got inside the water it still didn’t say I’m inside the water”, that really depends on when it actually did the ReadVoxels, before you hit the water or after?

If you want it to still detect even after jumping then you’d have to expand the region more or do it at least 2 studs below HRP (or even feet level).

It’s weird because it said I was swimming when I jumped :joy: I’ll try moving it down and just make sure they haven’t jumped recently as I don’t want any false positives.

Again, when you jump your humanoid may be still in the swimming state (even though it should say you’re jumping, could be because it hit the water as soon as you jumped), humanoid states aren’t that perfected nor is anything haha.

Hey I’m kinda new to this whole thing and was wondering where I would place this script to make it work

You put it in StarterCharacterScripts