How to detect when player camera is underwater

You’re kind of spamming by repeating posts to bump it.

4 Likes

Alright, so I’ve made a rough map of where it works, and it seems to only work in one direction and only in this small rectangle of space. Additionally, I am unable to make the water move because if I do, it will cease to function


We are making slow but sure progress. I’ll keep you updated on what I come up with next

Also, it only seems to work on 1 stud of 0y.


Any water above or below 0y will not be detected. This is a majour issue since water will and can be anywhere and everywhere in my game.

Here’s a diagramme as to what is happening.
What I want instead, is for all water to be detectable

1 Like

Still don’t got it… I’m extremely confused and have been working on this for more than two weeks without break…

1 Like

I copied a code like this before, it uses a function of terrain.Terrain:ReadVoxels()
It allows you to get an information of terrain in the region you give.

1 Like

For the final time I’d urge anyone including you to please please read my post!
I don’t know how many times I’ve said this but I don’t want to use readvoxels. It’s inaccurate!

1 Like

this is the copied code. Here it is:

local Work = game.Workspace
local Camera = Work.CurrentCamera
local Terrain = Work.Terrain

function CFrameToVector3(cframe)
	return Vector3.new(cframe.X,cframe.Y,cframe.Z)
end

while wait() do
	local REGION = Terrain:ReadVoxels(Region3.new(CFrameToVector3(Camera.CFrame)-Vector3.new(.0001,.0001,.0001),CFrameToVector3(Camera.CFrame)):ExpandToGrid(4),4)
	for Number, Information in pairs(REGION[1][1]) do
		for i,v in pairs(Work:GetDescendants()) do
			if Information == Enum.Material.Water and v:IsA("EqualizerSoundEffect") and v.Name == "Muffle" then
				v.Enabled = true
			elseif v:IsA("EqualizerSoundEffect") and v.Name == "Muffle" then
				v.Enabled = false
			end
		end
	end
end

Put this script inside StarterPlayerScript.
For sound, you can put it anywhere inside workspace but you need to add a EqualizerSoundEffect and name it muffle.

EqualizerSoundEffect Set up:
High Gain:-50
Low Gain:0
Mid Gain:-50
Enabled = false

From youtube.

1 Like
while true do
	local position = workspace.CurrentCamera.CFrame.Position
	
	local minimum = Vector3.new(position.X + 0.01, position.Y + 0.01, position.Z + 0.01)
	local maximum = Vector3.new(position.X - 0.01, position.Y - 0.01, position.Z - 0.01)
	local region = Region3.new(maximum, minimum)
	region = region:ExpandToGrid(4)
	
	if region then
		local material = workspace.Terrain:ReadVoxels(region, 4)
		if material[1][1][1] == Enum.Material.Water then
			print("in water")
		else
			print("not in water")
		end
	end
	wait(0.1)
end

this is the method I use, and it works perfectly fine, and in my case, it does account for waves, it prints in water when I’m in the waves, and prints not in water when I’m not in the water

edit: if you’re using more “agressive” waves, then this might not work, I’m using this for rather “calm” waves, I’ve set the waterwavesize to 0.35

1 Like

Exactly, thats what i’m trying to say,

You can also change the size of the region of the camera to be smaller to get more accurate reading off the the camera part or even resizing the camera part itself to being smaller so it more accurately responds to it.

The current size of the camera is roughly 2 by 4 in scale, if you scale that down by like i don’t know 2x or 3x it could get a more accurate reading of the part touching the terrain part.

1 Like

Waterwavesize is .35

1 Like

I have been looking for a solution to something like this for a while now, and it really is more complicated than it should be. I would suggest looking more into raycasting if the other solutions aren’t working for you.

On the other hand, Roblox honestly needs to redesign terrain water completely to make wave formations more realistic and include some sort of function that automatically detects when your camera clips underwater so something like this wouldn’t be so complicated to make accurately. I made something similar to this as a feature request in case you’re interested.

2 Likes

I completely agree. I’d also go on to say that water should rise a bit on coastal areas based on a value a person can set
image

2 Likes

I only briefly read the post, and none of the replies so I’m sorry if someone already said this.

Couldn’t you accomplish this with raycasting? Something like this:

local module = {}

local rayDistance = 5 -- you can change this if you want
local cam = game.Workspace.CurrentCamera
local userInput = game:GetService("UserInputService")
local player = game:GetService("Players").LocalPlayer

function module:CastWithWhitelist(whiteList)
	local mousePos = userInput:GetMouseLocation()
	local viewportMouseRay = cam:ViewportPointToRay(mousePos.X, mousePos.Y)
	local shortRayForWater =  Ray.new(viewportMouseRay.Origin, viewportMouseRay.Direction * rayDistance)
	return workspace:FindPartOnRayWithWhitelist(shortRayForWater, whiteList, false, false) --Make sure this last one is false
end

return module

I’ve never tried to detect water before, but it is a parameter for raycasting. This will cast a ray from the camera and if it hits water, should return it. I think you also need to pass this module {workspace.Terrain}.

1 Like

Are you sure if this can detect and take into account waves using educated guesses with equations as opposed to just brainless guessing and voxel reading? I’m 60% sure raycasts aren’t accurate with terrain unless it’s raycasted with a mouse

1 Like

I’m not 100% sure, no. It’s just my best guess. It’s a pretty quick test though, call this module passing it {workspace.Terrain} and use the returned value to test it.

[EDIT] You’ll probably want to change the distance so it isn’t capturing water when your camera is 5 studs above it.

1 Like

It seems the module has an error?
image
Distance is not defined
image

1 Like

Oh whoops! I was using some of my code as an example, so I forgot to delete that out. I’ll edit the code for you, but just replace distance with rayDistance

1 Like

I’m curious if this solved your problem or not, did it work?

The easy test is to spawn your character underwater and call this module passing it the terrain like this:

wait(5)--Lazy/quick way to wait for the player to load
local foundWater = yourRaycastingModule:CastWithWhitelist({workspace.Terrain})
if foundWater then
	print("Camera is underwater")
else
	print("Camera is not underwater")
end

If it works, then you’ll have to figure out a few more things before it will work the way you want it to. First, you’ll need to filter it even more so that it only detects water and not other terrain. Second, you’ll need to figure out when and where to call this module. Since it has to do with the camera, you’ll want to call it every frame. I suggest using a region3(or maybe hook it if the character state is swimming) as an event signal to start checking it every frame so that you aren’t needlessly checking for water in areas where there is no water. Lastly, you should change rayDistance to something less (I have absolutely no idea how small a ray can be, but I would experiment with things like 0.5, 0.1, 1 etc…). It should definitely work with a value of 1 since that’s the beginning length of the ray’s direction.

1 Like


image

It seems to be giving me another error.

Also, how is it that one filters out terrain? Is it as simple as checking the material?

1 Like

I’m not exactly sure what is causing that error. Maybe you’ll have to wait for the character to load instead of the wait(5).

As for your question, it should be as simple as doing this:

local object, position, normal, material = workspace:CastWithWhitelist({workspace.Terrain})
if object and object.Name == "Terrain" and material  == Enum.Material.Water then
	print("Camera is underwater")
end
1 Like

Made this script for a game back in 2017

game:GetService("RunService").RenderStepped:Connect(function(step)
	local pos = workspace.Terrain:WorldToCell(workspace.CurrentCamera.CoordinateFrame.p)
	local isInWater = workspace.Terrain:GetWaterCell(pos.X, (pos.Y), pos.z)
	
	if isInWater == true then
		-- if the camera is underwater, do something
	else
		-- if the camera is not underwater, do something else
	end
end)

This doesn’t account for waves, but using the formula above, could possibly work

2 Likes