Detecting Character Touching Water

  1. What do you want to achieve?

OK I KNOW IT SEEMS LIKE THIS ANSWER HAS ALREADY BEEN ANSWERED AS PUT A PART TO DETECT IT BUT ITS MORE COMPLICATED THAN THAT, I have a procedurally generated terrain script which makes cool WATER mountains


I have a script where if you are in water for a certain amount of time you DIE but since I am using procedurally generated terrain, it only works if you are under the base Y level. Is there a way I could do what I am doing with the terrain onto that water part?

  1. What solutions have you tried so far?

I tried looking through the devforum but all of them said you need to use a part since the FloorMaterial will detect air when under terrain. I made one where if you are in the air for a certain amount of time (since it thinks you are standing on air with terrain) and it works but there are some instances where due to the boat that I can not get into, it will make you in the air for several seconds. So I reverted back to the old one.

The script for procedually generated terrain is from https://www.youtube.com/watch?v=LsN_Z4ksO-c and this is my script:

local Players = game:GetService("Players")



------------------------------------------------------------------------------------------------------------------------------------------------



local BASE_HEIGHT 		= 100				-- The main height factor for the terrain.

local CHUNK_SCALE 		= 3 				-- The grid scale for terrain generation. Should be kept relatively low if used in real-time.

local RENDER_DISTANCE 	= 50				-- The length/width of chunks in voxels that should be around the player at all times

local X_SCALE 			= 90			-- How much we should strech the X scale of the generation noise

local Z_SCALE 			= 90			-- How much we should strech the Z scale of the generation noise

local GENERATION_SEED	= math.random(2^1025, 1) * -1 	-- Seed for determining the main height map of the terrain.

local TERRAIN_TYPE = Enum.Material.Water --Terrain Type

local PlayersService = game:GetService("Players");
local StringToDetect = "/seed";
local TextChatService = game:GetService("TextChatService");
local SEED = game:GetService("ReplicatedStorage"):WaitForChild("SEED")

PlayersService.PlayerAdded:Connect(function(Player)
	Player.Chatted:Connect(function(Message)
		if string.find(string.lower(Message), string.lower(StringToDetect)) then
			SEED:FireAllClients(GENERATION_SEED)
		end;
	end);
end);

------------------------------------------------------------------------------------------------------------------------------------------------



local chunks = {} --table to store chunk locations



--Checks player location to see if they have already been there

--If it is a new location, it adds to table

local function chunkExists(chunkX, chunkZ)

	if not chunks[chunkX] then

		chunks[chunkX] = {}

	end

	return chunks[chunkX][chunkZ]

end



--Takes calculated values and generates terrain

local function mountLayer(x, heightY, z, material)

	local beginY = -BASE_HEIGHT

	local endY = heightY

	local cframe = CFrame.new(x * 4 + 2, (beginY + endY) * 4 / 2, z * 4 + 2)

	local size = Vector3.new(4, (endY - beginY) * 4, 4)

	workspace.Terrain:FillBlock(cframe, size, material)	

end



--Prepares values for terrain generation

function makeChunk(chunkX, chunkZ)

	local rootPosition = Vector3.new(chunkX * CHUNK_SCALE, 0, chunkZ * CHUNK_SCALE)

	chunks[chunkX][chunkZ] = true -- Acknowledge the chunk's existance.

	for x = 0, CHUNK_SCALE - 1 do

		for z = 0, CHUNK_SCALE - 1 do

			local cx = (chunkX * CHUNK_SCALE) + x

			local cz = (chunkZ * CHUNK_SCALE) + z

			local noise = math.noise(GENERATION_SEED, cx / X_SCALE, cz / Z_SCALE)

			local cy = noise * BASE_HEIGHT

			mountLayer(cx, cy, cz, TERRAIN_TYPE) --sends values to mountLayer function

		end

	end

end





--Checks player surroundings

--Adds location to table

--Adds new terrain as player moves to new locations

function checkSurroundings(location)

	local chunkX, chunkZ = math.floor(location.X / 4 / CHUNK_SCALE), math.floor(location.Z / 4 / CHUNK_SCALE)

	local range = math.max(1, RENDER_DISTANCE / CHUNK_SCALE)

	for x = -range, range do

		for z = -range, range do

			local cx = chunkX + x

			local cz = chunkZ + z

			if not chunkExists(cx, cz) then --sends player location area to chunkExist function

				makeChunk(cx, cz) --If player is in a new area it will make terrain

			end

		end

	end

end



--Main Loop

--Gets player location every 1 second

while true do

	for _, player in pairs(Players:GetPlayers()) do

		if player.Character then

			local humanoidRootPart = player.Character:FindFirstChild("HumanoidRootPart")

			if humanoidRootPart then

				checkSurroundings(humanoidRootPart.Position) --sends player location to checkSurroundings function

			end

		end

	end

	wait(1)

end
1 Like

This made not work as you said the terrain gets seen as air, but you could possibly fire a raycastfrom the bottom how the character to check if there under or in water. Or another way with raycast is have a part in the water where it is never exposed to air and you can see if it is in water that way. But you may see right though these and see something I don’t

You can check if a player is submersed in water by utilising humanoid states:
if character.Humanoid:GetState() == Enum.HumanoidStateType.Swimming then

From there, you start counting the timer. There’s a problem with this though, by default players can hold jump to swim upwards, and if the player is at the water’s surface they can jump clean out of the water repeatedly to avoid being in the water.

Ultimately it’s up to you to decide how you want to design a solution around this, but my suggestion would be to have a boolean that is flipped to TRUE when the player enters the swimming state, so that if the player is in the Freefall humanoid state it keeps counting the timer upwards. This boolean will be set to FALSE when the Humanoid state is set to Running (grounded) or Climbing. In addition, in Freefall state, set the boolean back to FALSE after say 0.5s to avoid issues where the player is legitimately in the air for another reason than jumping to avoid the water temporarily, and still drowns.

this PROBABLY works but I simply have no idea how to raycast

This one I tried and it works, although not the second paragraph yet but I will try that!

I made a LocalScript inside of StarterPlayer > StarterCharacterScripts

local player = game:GetService("Players").LocalPlayer
local character = player.Character
local hum = character:WaitForChild("Humanoid")
ended = false
local remaining = player.PlayerGui.Meter.Background.Stamina1

repeat
	task.wait()
	if character.Humanoid:GetState() == Enum.HumanoidStateType.Swimming then
		ended = false
		remaining.Parent.Visible = true
		task.wait()
		repeat
			if remaining.Size.Y.Scale < 0 then
				player.PlayerGui.Meter.Background.Stamina1.Size = UDim2.new(1,0,0,0)
			end
			wait(1)
			player.PlayerGui.Meter.Background.Stamina1.Size = player.PlayerGui.Meter.Background.Stamina1.Size - UDim2.new(0, 0, .05, 0)
		until ended == true or remaining.Size.Y.Scale <= 0 or Enum.HumanoidStateType.Landed --almost forgot to add this
		if remaining.Size.Y.Scale <= 0 then
			hum.Health = 0
		end
	elseif character.Humanoid:GetState() == Enum.HumanoidStateType.Landed then
		ended = true
		repeat
			if remaining.Size.Y.Scale > 1 then
				player.PlayerGui.Meter.Background.Stamina1.Size = UDim2.new(1,0,1,0)
			end
			wait(1)
			player.PlayerGui.Meter.Background.Stamina1.Size = player.PlayerGui.Meter.Background.Stamina1.Size + UDim2.new(0, 0, .05, 0)
		until ended == false or remaining.Size.Y.Scale >= 1
	end
until nil

Edit: I used a useless humanoid variable so if you want you can just change character.Humanoid to hum

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