Not under of the terrain water just under of a water but its a part.
Hi!
I am making something relatively similar myself, so I decided to write you a short script. It has no visual effects, but you can always add GUIs, for example Minecraft like hearts, and so on.
The simplest way of detecting touches is using .Touched event. When it comes to really high speeds (e.g. bullet speeds), .Touched doesn’t run reliably, but is almost definitely good enough for water drowning effect. The most full-proof way involves Region3, but they demand too much resources for simple water drowning, especially when they are large.
Before I continue explaining, you can find the full code here:
Code
local water = script.Parent
water.Anchored = true; water.CanCollide = false
-- Settings --------------------------------
local DAMAGE_PER_SECOND = 8
local TIME_BEFORE_DAMAGE = 3 -- seconds
--------------------------------------------
local inWater = {}
water.Touched:Connect(function(object)
if (object.Name == "Head" and inWater[object.Parent] == nil) then
inWater[object.Parent] = TIME_BEFORE_DAMAGE
end
end)
water.TouchEnded:Connect(function(object)
if (object.Name == "Head") then
local n = inWater[object.Parent]
inWater[object.Parent] = nil
if (n < 3) then
repeat n += 1; wait(1)
until n >= TIME_BEFORE_DAMAGE or inWater[object.Parent] ~= nil
end
inWater[object.Parent] = n
end
end)
while (true) do
for char, count in pairs(inWater) do
if (count > 0) then
inWater[char] -= 1
else
char.Humanoid:TakeDamage(DAMAGE_PER_SECOND)
end
end
wait(1)
end
I’m using a table called inWater. It is a dictionary-type table, meaning indexes are not necessary numbers, and its form reminds us of a dictionary.
local inWater = {
[character here] = number of seconds here;
...
}
Indexes are characters that are in water, and seconds represent time interval before player starts “drowning”/loosing health points.
We only continue with the drowning function under the condition that head is under water (inside the part) and if head is not yet under water.
water.Touched:Connect(function(object)
if (object.Name == "Head" and inWater[object.Parent] == nil) then
-- further code
inWater[object.Parent] = TIME_BEFORE_DAMAGE
end
end)
We have an infinite while-loop, running every second. Set the time interval customly. The while-loop checks every index and element in the inWater table using pairs().
Remember this line?
inWater[object.Parent] = TIME_BEFORE_DAMAGE
Number is seconds, right? If player is not in water, that value is nil. Conversly, if player is in water, that value is a number value.
for char, count in pairs(inWater) do
if (count > 0) then
inWater[char] -= 1 -- Subtract 1 each second.
else -- Once value <= 0, player starts taking damage every second.
char.Humanoid:TakeDamage(DAMAGE_PER_SECOND)
end
end
Why do we even have the value stored. Because we don’t want player to start “drowning” immediately. Alright. But that value has even further functionality. Once player gets their head out of the water, that value slowly starts raising. We set it to nil, because head is not under water anymore, but we remember what the value was before. If player wast taking damage, that means it was zero, if player got out of the water before that, it means that it’s between zero and default.
local n = inWater[object.Parent] -- 1) Store the previous number.
inWater[object.Parent] = nil -- 2) Mark player as out of water.
if (n < 3) then
repeat n += 1; wait(1) -- 3) Restore the number every second (custom).
--[[
4) Continue until value is back to normal, or player
gets in water again.
]]
until n >= TIME_BEFORE_DAMAGE or inWater[object.Parent] ~= nil
end
--[[
When player is in water, timer is reset to default value:
that is why we have to set it to current value (n).
]]
inWater[object.Parent] = n
How do we detect whether player got out of the water? The opposite of .Touched is .TouchEnded event.
Hopefully this gives you a good start!