How to detect when player camera is underwater

I would politely urge you to reread my top post. I explicitly stated I do not want to read terrain voxels, as they are extremely inaccurate and are in a 3d grid of 4 studs per block, and do not take into account waves

Set wave speed to 0 and make a timestamp to base your water clock cycle off of(best guess to roblox’s internal water clock cycle), plug that in to a wave function

2 Likes

+poopnugget142 Frankly, that’s not from a script. Roblox has this built-in as part of their graphics system

+tyridge I still don’t get how this will apply to cameras. So if you’d get the camera’s position and apply the wave function to it, won’t the modified position just wobble around aimlessly? I don’t get how that would help me detect if the camera is underwater or not.

…? Not sure what you’re saying.

If you want to get if the camera is under water, you need to see if it’s below the surface height. Wave function and clock hack detects surface height on a 2 dimensional plane

But what would be the surface height? I want it so that it would be able to detect water in any position, not just a single one. From what I believe you’re saying is that the wave function would work relative to the camera, meaning the modified position would just bob up and down based on the wave function.

local campos = workspace.CurrentCamera.CFrame.p
local surfacebaseray = Ray.new(campos,Vector3.new(0,-5,0))
local waterhit,waterpos,_ = workspace:FindPartOnRayWithWhitelist(surfacebaseray,{workspace.Terrain})
if waterhit then
     local surfacebase = waterpos.Y + GetSurfaceHeightAtXZ(campos.x,campos.z)
     if campos.y < surfacebase then
        -- underwater
     end
end
1 Like

How do I implement this? I supposed I was supposed to put a print(“underwater”) in the - - underwater
but it doesn’t print it when the camera is underwater. I know this might seem impossible but it has been done multiple times, theme park tycoon 2, robloxian waterpark, ect.

Someone already linked you to the Surface Height function and you shot it down for being “needlessly complicated”

https://devforum.roblox.com/t/how-to-get-wave-height-at-a-specific-position/30672/3

4 Likes

Okay, now that I’ve implemented it it still doesn’t function.

local campos = workspace.CurrentCamera.CFrame.p
local surfacebaseray = Ray.new(campos,Vector3.new(0,-5,0))
local waterhit,waterpos,_ = workspace:FindPartOnRayWithWhitelist(surfacebaseray,{workspace.Terrain})
function GetSurfaceHeightAtXZ(x,z)
	return 1.8 * math.sin(2*math.pi/85 * x) * math.sin(2*math.pi/85 * z)
end
if waterhit then
     local surfacebase = waterpos.Y + GetSurfaceHeightAtXZ(campos.x,campos.z)
     if campos.y < surfacebase then
        print('a')
     end
end

I don’t know if what I did was wrong, please let me know

blobbybob’s solution on that post on the bottom is what you want. Keep in mind it seems to work best at a speed of 1 but with some trial and error and tinkering you should be able to get it working well with anything.

1 Like

What is supposed to be the third argument “Cycle”?

1 Like

Read the whole post, I’m on mobile now can’t really help much further

1 Like

Okay, so I see it’s supposed to be the current water cycle, but how do get the cycle? It doesn’t mention anywhere in his post how

1 Like

image

2 Likes
while wait() do
	if not _G.GetWaterWaveCycle then
		--We have to make sure the water's cycle is 0. There is no way to know this,
		--but if the WaterWaveSpeed is nonzero, there's a good chance the cycle is
		--nonzero.
		assert(workspace.Terrain.WaterWaveSpeed==0,
			"WaterWaveCycle cannot be defined after setting WaterWaveSpeed to"
			.."anything nonzero.");
		--This will not catch cases where the user changes the water wave speed from
		--a nonzero value to 0 then calls this code chunk, but I can't think of a way
		--to catch that case.
		--We just have to hope users use this correctly.
	
		local cycleAtLastSet = 0; --The cycle at the time of the last wave speed set.
		local tickAtLastSet = 0; --The tick() at the last WaterWaveSpeed set.
		local speedAtLastSet = 0; --The last WaterWaveSpeed which was set.
	
		--@return A float between 0 and 2pi indicating the current cycle.
		--@describe The cycle will be 0 when a place is freshly loaded and the
		--    WaterWaveSpeed is 0.
		function _G.GetWaterWaveCycle()
			return math.pi * 2 * ((cycleAtLastSet + (tick() - tickAtLastSet)
				* workspace.Terrain.WaterWaveSpeed / 85) % 1);
		end
	
		--Listen for changes to WaterWaveSpeed and update the state variables.
		workspace.Terrain.Changed:connect(function(prop)
			if prop == "WaterWaveSpeed" then
				cycleAtLastSet = (cycleAtLastSet + 
					(tick() - tickAtLastSet) * speedAtLastSet / 85) % 1;
				print("Current Cycle State: ", cycleAtLastSet);
				tickAtLastSet = tick();
				speedAtLastSet = workspace.Terrain.WaterWaveSpeed;
			end
		end)
	end
	local campos = workspace.CurrentCamera.CFrame.p
	local surfacebaseray = Ray.new(campos,Vector3.new(0,-5,0))
	local waterhit,waterpos,_ = workspace:FindPartOnRayWithWhitelist(surfacebaseray,{workspace.Terrain})
	local waveHeight = workspace.Terrain.WaterWaveSize * 2
	local waveWidth = 85
	function GetSurfaceHeightAtXZ(x, z, cycle)
		return waveHeight * math.sin(2*math.pi*x/waveWidth + cycle + math.pi/2)
			* math.sin(2*math.pi*z/waveWidth);
	end
	if waterhit then
	     local surfacebase = waterpos.Y + GetSurfaceHeightAtXZ(campos.x,campos.z)
	     if campos.y < surfacebase then
	        print('a')
	     end
	end
end

Okay, so now I’m simply confused as to why the waterwavespeed should be 0. Shouldn’t it not be 0 for the waves to move?

1 Like

Roblox’s internal clock increments when the speed isn’t 0, so if the speed is set to 0, then you change it to non zero, you can base the time elapsed since the change to get a guesstimate on the cycle. That’s what this does, is wait for you to set it to something other than 0, to set the tickAtLastSet variable to be used in GetWaterWaveCycle. But it needs to be 0 at the start

1 Like

Alright, so it kind of works, but the waterwavespeed has to be set to 0, meaning the water is frozen, and also, it only works in a small area, I don’t know why.

1 Like

I’m completely unsure why this is happening. It isn’t in the origin either.

1 Like

Why is it like this?
I still haven’t figured it out

1 Like

I haven’t figured it out quite yet

1 Like