Terrain:IsInWater(Vector3 position)

This is needed because using WorldToCellPreferEmpty or WorldToCellPreferSolid is not accurate enough to tell if your head is underwater or not.
If you stand on a shoreline it might say you’re underwater, if you swim in the sea it might say you’re above water.

This issue really makes it difficult to tell if a player is underwater or not.

P.S using Humanoid.Swimming is not accurate enough.

6 Likes

Is ReadVoxels not sufficient? You can specify how small the resolution is.

Here’s a use case for when the admins eventually ask: Drowning mechanic.

GetWaterCell can still be used for this case.


???

The legacy terrain API still has backwards compatibility with smooth terrain, though I’m not sure how long this will be supported.

Its just as inaccurate as the other two stated methods except here you also need to convert your position to terrain coordinates.

How precise are you trying to get it?

± 0.1 studs would be good enough.
Converting to terrain coordinates is ± 2 studs

Including the water waves?

No thats not needed.

I don’t really understand how Humanoid.Swimming isn’t precise enough for you, but I guess you could try this…

local function terrainCast(origin,direction)
	local ignoreList = {}
	for _,v in pairs(workspace:GetChildren()) do
		if v ~= workspace.Terrain then
			table.insert(ignoreList,v)
		end
	end
	local r = Ray.new(origin,direction)
	return workspace:FindPartOnRayWithIgnoreList(r,ignoreList)
end

local function isUnderWater(humanoid)
	local char = humanoid.Parent
	if char then
		local head = char:FindFirstChild("Head")
		if head then
			local hit,pos,norm,mat = terrainCast(head.Position + Vector3.new(0,1000,0),Vector3.new(0,-1000,0))
			if hit and mat == Enum.Material.Water then
				return true
			end
		end
	end
	return false
end

This would suffocate me if I were in an underground cave that has water above it.

P.S. I do have underground caves with water above it.

Alright, maybe try this?

local function terrainCast(origin,direction)
	local ignoreList = {}
	for _,v in pairs(workspace:GetChildren()) do
		if v ~= workspace.Terrain then
			table.insert(ignoreList,v)
		end
	end
	local r = Ray.new(origin,direction)
	return workspace:FindPartOnRayWithIgnoreList(r,ignoreList)
end

local function isUnderWater(humanoid)
	local char = humanoid.Parent
	if char then
		local head = char:FindFirstChild("Head")
		if head then
			local _,ceilingPos = terrainCast(head.Position,Vector3.new(0,1000,0))
			local hit,pos,norm,mat = terrainCast(ceilingPos,Vector3.new(0,-1000,0))
			if hit and mat == Enum.Material.Water then
				return true
			end
		end
	end
	return false
end
1 Like

It would work, if u change -1000 to the distance from head to ceilingpos.
But it would be VERY laggy, given you have to ask this atleast 2 times per second.

The method I use now works, but it’s not very good. But its more efficient than this.
I still think IsInWater is a crucial function that many more than just me uses.

I beg to differ tbh.
I’m not sure why you need it to be this precise, are you sure you aren’t doing something incorrectly?

A person whos head is above water doesnt drown.
Using the current terrain functions he will.

I need to check this often because you get hurt once per second ur underwater once u run out of air.
If you jump once on the water surface then this check might miss it unless it goes atleast twice per second.

cc @Quenty, since he did this with Whatever Floats your Boat.

2 Likes

For me it’s just a Y axis check to determine underwater state as all my water is flat.

This seems like a solid feature request, as we run into this issue with checking if parts are on fire or not too.

Right now the standard way to dynamically do this is a raycast to grab material and/or a cell check, although this is pretty expensive and not too accurate in many cases.

4 Likes