Checking if the player is under the water and not just floating in it?

Hello, So I am working a fast open sea game where I am working on an oxygen system but I have ran into a problem my system takes away oxygen even if the player is floating at the top of the water.
Takes away oxygen here when it’s not meant to.


And this is where its only meant to take away oxygen.

I have tried thinking of ways around this but no of them seem to work.
Here is my script.

local bar = script.Parent.BarBG.Bar

local maxOxygen = 100
local currentOxygen = maxOxygen

local timePerLoss = 0.3

local isSwimming = false

local hum = game.Players.LocalPlayer.Character:WaitForChild("Humanoid")

hum.StateChanged:Connect(function(oldState, newState)
	isSwimming = (newState == Enum.HumanoidStateType.Swimming)
end)

while wait(timePerLoss) do
	if isSwimming then
		currentOxygen = math.max(currentOxygen - 1, 0)
	else
		currentOxygen = math.min(currentOxygen + 1, maxOxygen)
	end

	if currentOxygen < 1 then
		hum.Health = 0
	end

	bar.Size = UDim2.new(currentOxygen / maxOxygen, 0, 1, 0)
end

Any help is appreciated.
Many Thanks for any help!

You could raycast downwards from the head (or mouth) and see if the ray intersects with the water surface to determine if the player is still breathing. Use it in conjunction with the method you already have.

I have tried this before but I just get a lot of lag is there a way to use this with out creating so much lag?

It really shouldn’t. I’ve been able to do thousands of raycasts per frame without lag. You would only need to do one raycast per frame for this scenario.

If it’s not too much hassle could you give me an example on how you would cast the ray?

I think this question has already been answered here:

Here’s a demo for you:
swimming.rbxl (41.5 KB)

(Look at the attribute in the video below)

local lp = game:GetService('Players').LocalPlayer
local char = lp.Character or lp.CharacterAdded:Wait()
local head: BasePart = char:WaitForChild('Head')
local hum: Humanoid = char:WaitForChild('Humanoid')

lp.CharacterAdded:Connect(function(ch)
	char = ch
	head = ch:WaitForChild('Head')
	hum = ch:WaitForChild('Humanoid')
end)

local mouthOffset: CFrame = CFrame.new(0, -0.25, -1) --Offset the origin position to near the mouth
local raycastDir: Vector3 = Vector3.yAxis * -5 --the raycast will only be 5 studs long
local rcp: RaycastParams = RaycastParams.new()
rcp.FilterDescendantsInstances = {workspace:WaitForChild('Terrain')}
rcp.FilterType = Enum.RaycastFilterType.Whitelist
rcp.IgnoreWater = false

game:GetService('RunService').RenderStepped:Connect(function()
	--check if the player is swimming but NOT breathing (which is when the raycast is nil)
	local isSwimming: boolean = hum:GetState() == Enum.HumanoidStateType.Swimming
		and not workspace:Raycast((head.CFrame*mouthOffset).Position, raycastDir, rcp)
	
	char:SetAttribute('IsSwimming', isSwimming)
end)
6 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.