Help needed with Bugged Water system

Hey, I am trying to make a custom water system that activates when you touch a part. Right now, there are 3 parts involved 1 for sound Fx (white), 1 for when you enter the water(Dark Blue) & one for when you leave (Light blue). It works by detecting a collision. when these 3 are separated it works fine but the problem is I need them to be close together. I have tried adding a debounce & I have even tried changing some collision groups around, but nothing seems to work. sometimes it picks up 2-3 collisions even though I added a debounce.

Here is a video demonstrating the problem:

I have seen some tutorials but they all show you how to make a different type of water system. in my water system you do not swim.

Enter Water script:

local IsTouched = false

local Brick = script.Parent

local PhysicsService = game:GetService("PhysicsService")

local Underwater = "Underwater"

function Water(hit)

	if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid") then
		

		local  p = game.Players:GetPlayerFromCharacter(hit.Parent)
		
			if not IsTouched then
			IsTouched = true
			wait()
			print("entered water")
			p.Character.PrimaryPart.CollisionGroup = Underwater
			
			local breath = p.PlayerGui:WaitForChild("breathscript")
			breath.Disabled = false
			breath.Parent.Air.TextLabel.GUI.Disabled = false
			breath.Parent.Air.TextLabel.Text = " "
			
			

			
			p.PlayerGui.Air.TextLabel.Visible = true	
			p.PlayerGui.underwater.Frame.Visible = true

			
			
			local hum = p.Character.HumanoidRootPart
			local fx = 	hum.BubbleSplash
			fx.Enabled = true
			
			wait(0.1)
				IsTouched = false
				 
			end
		end
	end
	
Brick.Touched:Connect(Water)

Leave water script:

local rep = game:GetService("ReplicatedStorage")

local FiredEvent = game.ReplicatedStorage.RemoteEvents:WaitForChild("AboveWater")

local IsTouched = false

local Brick = script.Parent


local PhysicsService = game:GetService("PhysicsService")

local AboveWater = "AboveWater"


function Water(hit)

if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid") then
	
	
		local   p = game.Players:GetPlayerFromCharacter(hit.Parent)
		
			if not IsTouched then 
			IsTouched = true
			wait()
			print("left water")
			p.Character.PrimaryPart.CollisionGroup = AboveWater

			local air = p.PlayerGui:WaitForChild("addair")
			local part = script.Parent
			local breath = p.PlayerGui:WaitForChild("breathscript")

			breath.Disabled = true
			
			
			
			FiredEvent:FireClient(p)
			
			
			
			p.PlayerGui.Air.TextLabel.Visible = false
			p.PlayerGui.underwater.Frame.Visible = false
			
			
			local hum = p.Character.HumanoidRootPart
			local fx = 	hum.BubbleSplash
			fx.Enabled = false

			
			local FiredPart = rep.RemoteEvents:FindFirstChild("AirEvent")

			FiredPart:FireClient(p, part)
	wait(0.1)
			
			
				IsTouched = false

			end
		end
	end
Brick.Touched:Connect(Water)

The sound Fx script is not bugged at all.

local debounce = false

local function Water(hit)
	if hit.Parent:FindFirstChild("Humanoid") then
		if not debounce then
			debounce = true
			wait(0.5)
			-- your code here
			debounce = false
		end
	end
end

Brick.Touched:Connect(Water)

The code I provided should work fine. The only thing I can think of is that you might have more than one part touching the water part at the same time. You could try increasing the debounce time to see if that helps.

1 Like

Why you can’t use everything in one part? If you need water leave, then use TOuchEnded function.

1 Like

I just tried this but this only makes it even more buggy

Here is the code for that:

local Brick = script.Parent

local Underwater = "Underwater"

local rep = game:GetService("ReplicatedStorage")

local FiredEvent = game.ReplicatedStorage.RemoteEvents:WaitForChild("AboveWater")

local Brick = script.Parent



local AboveWater = "AboveWater"



script.Parent.Touched:Connect(function(hit)

	if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid") then


		local  p = game.Players:GetPlayerFromCharacter(hit.Parent)

			wait()
			print("entered water")
			p.Character.PrimaryPart.CollisionGroup = Underwater

			local breath = p.PlayerGui:WaitForChild("breathscript")
			breath.Disabled = false
			breath.Parent.Air.TextLabel.GUI.Disabled = false
			breath.Parent.Air.TextLabel.Text = " "




			p.PlayerGui.Air.TextLabel.Visible = true	
			p.PlayerGui.underwater.Frame.Visible = true



			local hum = p.Character.HumanoidRootPart
			local fx = 	hum.BubbleSplash
			fx.Enabled = true


		end


end)
script.Parent.TouchEnded:Connect(function(hit)

	if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid") then


		local   p = game.Players:GetPlayerFromCharacter(hit.Parent)

			wait()
			print("left water")
			p.Character.PrimaryPart.CollisionGroup = AboveWater

			local air = p.PlayerGui:WaitForChild("addair")
			local part = script.Parent
			local breath = p.PlayerGui:WaitForChild("breathscript")

			breath.Disabled = true



			FiredEvent:FireClient(p)



			p.PlayerGui.Air.TextLabel.Visible = false
			p.PlayerGui.underwater.Frame.Visible = false


			local hum = p.Character.HumanoidRootPart
			local fx = 	hum.BubbleSplash
			fx.Enabled = false


			local FiredPart = rep.RemoteEvents:FindFirstChild("AirEvent")

			FiredPart:FireClient(p, part)
			wait(0.1)


		end
end)

This does not work. It basically is the same thing I wrote except with a local Infront of the function. and a few name changes I tried it though. here is the code:

Enter water script:

local PhysicsService = game:GetService("PhysicsService")

local Underwater = "Underwater"


local Debounce = false


local function Water(hit)

	if hit.Parent:FindFirstChild("Humanoid") then
		
		if not Debounce then
			Debounce = true
			wait(0.5)
		local  p = game.Players:GetPlayerFromCharacter(hit.Parent)
		


			print("entered water")
			p.Character.PrimaryPart.CollisionGroup = Underwater
			
			local breath = p.PlayerGui:WaitForChild("breathscript")
			breath.Disabled = false
			breath.Parent.Air.TextLabel.GUI.Disabled = false
			breath.Parent.Air.TextLabel.Text = " "
			
			

			
			p.PlayerGui.Air.TextLabel.Visible = true	
			p.PlayerGui.underwater.Frame.Visible = true

			
			
			local hum = p.Character.HumanoidRootPart
			local fx = 	hum.BubbleSplash
			fx.Enabled = true
			
			wait(0.1)
				Debounce = false
				 
			end
		end
	end
	
Brick.Touched:Connect(Water)

Leave water script:

local rep = game:GetService("ReplicatedStorage")

local FiredEvent = game.ReplicatedStorage.RemoteEvents:WaitForChild("AboveWater")


local Brick = script.Parent


local PhysicsService = game:GetService("PhysicsService")

local AboveWater = "AboveWater"




local Debounce = false

local function Water(hit)

	if hit.Parent:FindFirstChild("Humanoid") then
		
		if not Debounce then
			Debounce = true
			wait(0.5)
	
		local   p = game.Players:GetPlayerFromCharacter(hit.Parent)
		

			print("left water")
			p.Character.PrimaryPart.CollisionGroup = AboveWater

			local air = p.PlayerGui:WaitForChild("addair")
			local part = script.Parent
			local breath = p.PlayerGui:WaitForChild("breathscript")

			breath.Disabled = true
			
			
			
			FiredEvent:FireClient(p)
			
			
			
			p.PlayerGui.Air.TextLabel.Visible = false
			p.PlayerGui.underwater.Frame.Visible = false
			
			
			local hum = p.Character.HumanoidRootPart
			local fx = 	hum.BubbleSplash
			fx.Enabled = false

			
			local FiredPart = rep.RemoteEvents:FindFirstChild("AirEvent")

			FiredPart:FireClient(p, part)
	wait(0.1)
			
			
				Debounce = false

			end
		end
	end
Brick.Touched:Connect(Water)

keep in mind these are 2 separate bricks but both of them are not working properly.
I tried making them into 1 brick with 1 script like @GamEditoPro said but it did not work.

  1. You have TONS of useless stuff in your script.
    Instead of creating tons of variables for every 1 need you can either group them in array, but in your situation, you don’t need any of that string variables like Underwater and AboveWater, and just set your collision group with string, not variable:
BasePart.CollisionGroup = "Underwater"
  1. I’ll heavilly reccomend you use ModuleScripts instead of disabling any Scripts/LocalScripts in cases like your. Disabling scripts can break TONS of stuff, bc script after re-enabling will lose EVERYTHING it did. (I’m talking about things like counters)
  2. Basically your waits are useless and can be main reason of bugs, bc yielding can break your logic combinations. So try delete them (Or comment).
1 Like

Hey, Soo I cleaned up the script, I edited some stuff so that I don’t have to enable/disable anything at all. I took the waits out. But it still is very bugged. I have it as 2 parts one to enter and one to leave. It is the same bug as the other videos.

image

image

here is the enter water script:

local Brick = script.Parent

local PhysicsService = game:GetService("PhysicsService")

--local debounce = false 

function Water(hit)

--	if debounce then return end 

	if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid")  then
-- debounce = true 

		local  p = game.Players:GetPlayerFromCharacter(hit.Parent)
		
		
			if hit.Parent.PrimaryPart.CollisionGroup == "Default" or hit.Parent.PrimaryPart.CollisionGroup == "AboveWater"  then 
			
			print "default" 
			p.Character.PrimaryPart.CollisionGroup = "Underwater"

			local breath = p.PlayerGui:WaitForChild("breathscript")


			breath.Breath.Value = 25



			p.PlayerGui.Air.AirLeft.Visible = true	
			p.PlayerGui.underwater.Frame.Visible = true


			
		end

		
--		wait(1)
--		debounce = false
end
end 
Brick.Touched:Connect(Water)

.
.
.
.
.

here is the leave water script:

local Brick = script.Parent

local PhysicsService = game:GetService("PhysicsService")

---local debounce = false 

function Water(hit)
	
	
--	if debounce then return end 
	
	if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid") then 
 
---debounce = true 
		local  p = game.Players:GetPlayerFromCharacter(hit.Parent)
		
		
			if hit.Parent.PrimaryPart.CollisionGroup == "Underwater" then 
			
			print "UnderWater" 
			
			p.Character.PrimaryPart.CollisionGroup = "AboveWater"

			local breath = p.PlayerGui:WaitForChild("breathscript")


			breath.Breath.Value = 30



			p.PlayerGui.Air.AirLeft.Visible = false	
			p.PlayerGui.underwater.Frame.Visible = false





			
		end

		
--		wait(1)
--		debounce = false 
end
end 
Brick.Touched:Connect(Water)

this is starting to become stressful lol :rofl:

Why you can’t combine this leave and enter water scripts into one?
Also, in your case I think you need check if HEAD is left or entered water. This may be one of reasons, bc for example 2 parts of same model eaxh can trigger function.

1 Like

Hey, I really thought checking the head would work but it did not. it does work if I go through it slowly but this has been the case all along, but I need to be able to go through it at any speed as the speed increases when you dash and I might add speed boosters and stuff. I don’t want to make it all in one part because there are other touch events that need to happen like the big air bubble for example.
Also If I need to transfer the player to a level where they spawn underwater it is easier to make them fall on a part like in my game ex.

I currently have it working by duplicating the enter and exit parts several times but this is super buggy as you can see here:

Maybe you can take a look?
Buggy Water - Roblox

Touched event is buggy. You should consider to use OverlapParams instead

1 Like

If you are in a hurry and want to skip on how to use Overlap Params, you can always use this module:
ZonePlus v3.2.0 | Construct dynamic zones and effectively determine players and parts within their boundaries - Resources / Community Resources - DevForum | Roblox

1 Like

Hey, I have been checking this out, it seems very complicated. also, I don’t want to have a giant part that acts as a region I want to have small parts that detect when you enter and exit.
ex:
image

Hm… The only thing I can say is you should use:

Or you should use constant raycast from 6 sides of your character to detect is it in water part.
In your situation you will be unable to normally check if player is inside water or he left it. And you will be unable to make parts thin. Want know why?


Object’s positions can be changed only each heartbeat (frame). It’s defined by velocity of object. Due to this system, you can see smth very close to move, but actually they "teleport" a bit each time, so your character can just skip your thin part without actually touching it. There’s where your problem.

1 Like

Hey man, you think you can give me a example? I made the part bigger and duplicated it alot so it detects it more. I also changed it back to detecting everything because the head is too small I think. It almost works. Sometimes it does sometimes it doesn’t it is working more tho.

Take a look at the place I have copying enabled:

There is also another script in starterGUI but I am sure this does not effect anything.

Now you have more funny situation, where you can touch BOTH parts at the same time, and due to this BOTH Leave and Start water even are trigger, and they will work by random order, and this’s another reason of bugs. So add spacing between that parts.
(To guaranteed detection your part thinkness and distance between them should be Walkspeed / HeartbeatPerSecond + CharacterMaxLength, so with 60 HPS and 200 Walkspeed you need make them 3.3333333333333333… + CharacterMaxLength studs)
But I think your game won’t have constant speed so you need use 1 GIANT part with touch ended event. This’s THE ONLY WAY TO SOLVE YOUR PROBLEM. If you want that thin parts, just make giant part invisible and thin ones visible.

1 Like

Ok , im going to try to make 1 Big Giant part
I will let you know how it goes! I dont think it is convenient for me tho. Do I have to use overlap params I feel like Zone-plus has way too many scripts that I wont use. First Im going to try the distance.

@Crayemi32

your easiest solution is probably using zoneplus. you can allocate an entire folder of water parts to act as an ocean, where when inside ocean zone, then the player is in water, if not inzone, then the player is not in water.

sample code:


local Zone = require(ReplicatedStorage.Zone) --Zone module location

local OceanGroup1 = game.Workspace.map.Locations.Folder1 -- the group of parts as the ocean

local OceanZone1 = Zone.new(OceanGroup1) -- determine it as a Zone within zone module.

OceanZone1.playerEntered:Connect(function(player)
--whatever u want when player enters the area
end)

OceanZone1.playerExited:Connect(function(player)
--whatever u want when player leaves the area
end)
1 Like

This works! only one problem. How would I go about putting a zone within a zone. Like a safe zone.

The way I have it set up is when you exit the water everything goes back to normal however I have an underwater bubble that is a safe zone, and everything is not intended to go back to normal when you are in the bubble.

this is the way I have it set up:

this almost always works but if you look around the 0.7 second mark you can see it does notI’d say every one out of 50 times it does not. although I think I know a work around for this (by adding more parts) but I’m sure there Is a more convenient way. is there?

I got it working I realized I did not have to do all that extra stuff the zones work within the zones thats awesome! thanks!

1 Like

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