How to detect player's Head is under water?

Issue:

I cannot find a way to detect if the player’s head is underwater. I tried using ReadVoxels for the player’s Head, but it still does not detect if the Head is above the surface of the water or beneath. RayCasting does not work because it hits the skybox and returns nil.

Code:

MaterialDetector (local script) within StarterCharacter
-- Services
local Terrain = workspace.Terrain
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

-- Character references
local char = script.Parent
local head = char:WaitForChild("Head")
local hum = char:WaitForChild("Humanoid")

-- Function to check if head is underwater
local function isHeadUnderwater()
	
	-- Define the region around the head to check if it's submerged in water
	local min = head.Position - (0.5 * head.Size)
	local max = head.Position + (0.5 * head.Size)
	local region = Region3.new(min, max):ExpandToGrid(4)
	local material = Terrain:ReadVoxels(region, 4)[1][1][1]
	return material == Enum.Material.Water
end

-- Detect water status for the player every 0.5 seconds
while task.wait(0.5) do
	if isHeadUnderwater() then
		print("Underwater")
	else
		print("Not in water")
	end
end

Image:

I mean, this might not be the best performance wise, however I suppose you could use spatial queries.
I dont really know tho

1 Like

I have a method that may work.
Insert an invisible, cancollide false part in the same area as your terrain water 1 stud thick with the top surface resting at the water’s surface. Put them inside a folder then try raycasting. Since you have baseparts the raycast should now work when firing vertically from the character’s head. If a water block is above them it should be returned.
(Don’t forget character hats may also mess with the raycast so either start the cast above the head or add all accessory parts into a blacklist.

2 Likes