I found the best method for getting players server side (as attempting to damage a humanoid on local side is wrong), is to connect an event to both when the player joins, and they get a character. After that, a constant check is operated 1-5 times a second, checking to see if they are in water.
For the check itself, as mentioned by DarkDanny04, are the region and the humanoid state. To quantify this, I decided to have a longer check whilst not in water, then once in water, the check is decreased so that there is not as much resources used until the player is at the water (you could, just make the loop without a second loop inside, for easier coding). For the region method, you define the region and expand it to the grid that the terrain uses (size of 4x4x4) and then check the voxel. For more information on exactly how reading voxels works, I'd suggest reading this: Terrain:ReadVoxels If it's a water material, it will continue to damage them until the check says it is not. Here's the code for the region checking:
local debounce = false
game.Players.PlayerAdded:Connect(function(plr)
plr.CharacterAdded:Connect(function()
local Char = plr.Character
print("character made")
local Humanoid = Char:WaitForChild("Humanoid")
while Humanoid.Health > 0 do
wait(0.2)
local State = Humanoid:GetState()
local head = plr.Character:WaitForChild("Head")
local root = plr.Character:WaitForChild("HumanoidRootPart")
local humRegion = Region3.new(Vector3.new(root.Position.X - 0.5 , root.Position.Y - 1, root.Position.Z - .5), Vector3.new(root.Position.X +.5 , root.Position.Y +1, root.Position.Z +.5) ):ExpandToGrid(4)
local material = workspace.Terrain:ReadVoxels(humRegion,4)[1][1][1]
if material == Enum.Material.Water and plr.Character.Humanoid.FloorMaterial == Enum.Material.Air and debounce == false then
debounce = true
local underwater = true
while underwater == true do
print("Underwater")
Char.Humanoid:TakeDamage(5)
humRegion = Region3.new(Vector3.new(root.Position.X - 0.5 , root.Position.Y - 1, root.Position.Z - .5), Vector3.new(root.Position.X +.5 , root.Position.Y +1, root.Position.Z +.5) ):ExpandToGrid(4)
material = workspace.Terrain:ReadVoxels(humRegion,4)[1][1][1]
if material ~= Enum.Material.Water or plr.Character.Humanoid.FloorMaterial ~= Enum.Material.Air then
underwater = false
print("Not underwater")
end
wait(0.1)
end
debounce = false
else
wait(0.1)
end
end
end)
end)
(Please be aware I'm not full adept in terrain and voxels, so that method's code is a little all over the place)
The other method, humanoid:getstate() is a lot more simplistic, but doesn't return positive if the player is standing on land in water. You may want this if you have shallow water on your map as well as the deeper oceans (To get around the problem of stand on the water you want to damage, make it deeper).
game.Players.PlayerAdded:Connect(function(plr)
plr.CharacterAdded:Connect(function()
local Char = plr.Character
print("character made")
local Humanoid = Char:WaitForChild("Humanoid")
while Humanoid.Health > 0 do
wait(0.2)
local State = Humanoid:GetState()
if State == Enum.HumanoidStateType.Swimming then
print("Started swimming")
while State == Enum.HumanoidStateType.Swimming do
print("Swimming")
State = Humanoid:GetState()
Humanoid:TakeDamage(5)
wait(0.1)
end
print("No longer swimming")
end
end
end)
end)
Personally, I'd use the humanoid state for flexibility on shallow water, and simpler code, But it's up to you. Here's an uncopylocked game where you can see it all and just copy either scripts.
Hope this has been helpful.
4 comments
Can you show how you set up the scripts and where?
This is the only script.
Is the water terrain? Because if it is just a part, you could have the part not able to collide, and then just add a touched event to that
Why do you need to detect when the Script's Parent object gets changed? And why do you need to detect if its parent is a Model??? Why are you retrieving the player by looking for a player with the same name as the character(localplayer exists)??? Water isn't a valid material for parts iirc, and you don't need a debounce array since this is for every individual player.