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.