I have an oxygen bar and it was working before, but now I realized that its not doing anything anymore when I go under my terrain water. Here is what I have:
LocalScript inside GUI:
local player = game.Players.LocalPlayer
local Hum = player.Character:WaitForChild("Humanoid")
local background = script.Parent
local Bar = background:WaitForChild("Bar")
local OxygenText = background:WaitForChild("OxygenText")
OxygenText.Size = UDim2.new(1,0,1,0)
local oxyValue = player:WaitForChild("OxygenValue")
while wait(0.3) do
Bar:TweenSize(UDim2.new(player.OxygenValue.Value/1000,0,1,0), "Out", "Quad", 0.3)
OxygenText.Text = player.OxygenValue.Value.."/1000"
if OxygenText.Text == "0/1000" then
Hum.Health = 0
end
end
Script in ServerScriptService:
game.Players.PlayerAdded:Connect(function(player)
local increaseRate = 3
local decreseRate = 2
local oxygen = Instance.new("IntValue", player)
oxygen.Value = 1000
oxygen.Name = "OxygenValue"
wait(3)
while wait() do
local char = player.Character or player.CharacterAdded:Wait()
local castRay = Ray.new(char.Head.Position, Vector3.new(0,-10,0))
local whiteList = {char}
local hit, hitposition = workspace:FindPartOnRayWithIgnoreList(castRay, whiteList)
local underwater = false
if not hit or not hit:IsA("Terrain") then
local castRay2 = Ray.new(char.Head.Position + Vector3.new(0,1000,0), Vector3.new(0,-1000,0))
local hit2, hit2position = workspace:FindPartOnRayWithIgnoreList(castRay2, whiteList)
if hit2 and hit2:IsA("Terrain") then
underwater = true
end
end
if underwater == false then
oxygen.Value = math.clamp(oxygen.Value + increaseRate, 0, 1000)
else
oxygen.Value = math.clamp(oxygen.Value - decreseRate, 0, 1000)
end
end
end)
Are you getting any sort of errors? When I set up a sample place, it seemed to function as intended (oxygen went down in water and back up when out of water. I died at 0 oxygen).
One suggestion that may or may not help is to set up a more robust initializer on the server: if your game has Workspace.SignalBehavior set to Deferred, testing in Studio can sometime have your player join before running code that listens for that event (I believe this problem is exlusive to that setting, but it may happen in other cases). This results in none of your player initializer scripts running for that player, but it can be remedied with a small amount of code, like this:
--Declare the initializer as a first-class function.
local function player_added(player)
local increaseRate = 3
local decreseRate = 2
local oxygen = Instance.new("IntValue", player)
oxygen.Value = 1000
oxygen.Name = "OxygenValue"
wait(3)
while wait() do
local char = player.Character or player.CharacterAdded:Wait()
local castRay = Ray.new(char.Head.Position, Vector3.new(0,-10,0))
local whiteList = {char}
local hit, hitposition = workspace:FindPartOnRayWithIgnoreList(castRay, whiteList)
local underwater = false
if not hit or not hit:IsA("Terrain") then
local castRay2 = Ray.new(char.Head.Position + Vector3.new(0,1000,0), Vector3.new(0,-1000,0))
local hit2, hit2position = workspace:FindPartOnRayWithIgnoreList(castRay2, whiteList)
if hit2 and hit2:IsA("Terrain") then
underwater = true
end
end
if underwater == false then
oxygen.Value = math.clamp(oxygen.Value + increaseRate, 0, 1000)
else
oxygen.Value = math.clamp(oxygen.Value - decreseRate, 0, 1000)
end
end
end
local players = game:GetService("Players")
--Hook into the event like normal.
players.PlayerAdded:Connect(player_added)
--Initialize any players that may have been able to join before the event connection was made.
for _,player in ipairs(players:GetPlayers()) do
player_added(player)
end
I don’t have any errors and mine used to do the exact same thing yours did and I checked the setup of my water and everything but I never changed anything
I think I figured out a potential issue: the code can’t differentiate between the surface of water and the bottom of a terrain lake. If you are in water–even if completely submerged–but close to terrain ground at the bottom, the code will think you’re above water.
It doesn’t seem like the old raycast methods are able to make this distinction. This code uses the newer methods and should be able to differentiate between terrain ground and the surface of the water:
local water_material = Enum.Material.Water
local V3_n10 = Vector3.new(0,-10,0)
local V3_1000 = Vector3.new(0,1000,0)
local V3_n1000 = Vector3.new(0,-1000,0)
--Declare the initializer as a first-class function.
local function player_added(player)
local increaseRate = 3
local decreseRate = 2
local oxygen = Instance.new("IntValue", player)
oxygen.Value = 1000
oxygen.Name = "OxygenValue"
wait(3)
local raycast_params = RaycastParams.new()
raycast_params.FilterType = Enum.RaycastFilterType.Blacklist
while wait() do
local char = player.Character or player.CharacterAdded:Wait()
raycast_params.FilterDescendantsInstances = {char}
local collision = workspace:Raycast(char.Head.Position,V3_n10,raycast_params)
local underwater = false
if not (collision and collision.Material == water_material) then
collision = workspace:Raycast(char.Head.Position + V3_1000,V3_n1000,raycast_params)
if collision and collision.Material == water_material then
underwater = true
end
end
if underwater == false then
oxygen.Value = math.clamp(oxygen.Value + increaseRate, 0, 1000)
else
oxygen.Value = math.clamp(oxygen.Value - decreseRate, 0, 1000)
end
end
end
local players = game:GetService("Players")
--Hook into the event like normal.
players.PlayerAdded:Connect(player_added)
--Initialize any players that may have been able to join before the event connection was made.
for _,player in ipairs(players:GetPlayers()) do
player_added(player)
end
Oh yea that happened I have a secret room and you lose oxygen when you’re in it even tho there’s water above you and you’re not in water but I fixed that by disabling it until you get out and then once you get out the oxygen will just refill
Another issue seems to be if a block is above the player, as it would block the “underwater check” raycast. This code will resolve that issue:
local water_material = Enum.Material.Water
local V3_n10 = Vector3.new(0,-10,0)
local V3_1000 = Vector3.new(0,1000,0)
local V3_n1000 = Vector3.new(0,-1000,0)
local raycast_params = RaycastParams.new()
raycast_params.FilterType = Enum.RaycastFilterType.Whitelist
raycast_params.FilterDescendantsInstances = {workspace.Terrain}
local function player_added(player)
local increaseRate = 3
local decreseRate = 2
local oxygen = Instance.new("IntValue", player)
oxygen.Value = 1000
oxygen.Name = "OxygenValue"
wait(3)
while wait() do
local char = player.Character or player.CharacterAdded:Wait()
local collision = workspace:Raycast(char.Head.Position,V3_n10,raycast_params)
local underwater = false
if not (collision and collision.Material == water_material) then
collision = workspace:Raycast(char.Head.Position + V3_1000,V3_n1000,raycast_params)
if collision and collision.Material == water_material then
underwater = true
end
end
if underwater == false then
oxygen.Value = math.clamp(oxygen.Value + increaseRate, 0, 1000)
else
oxygen.Value = math.clamp(oxygen.Value - decreseRate, 0, 1000)
end
end
end
local players = game:GetService("Players")
players.PlayerAdded:Connect(player_added)
for _,player in ipairs(players:GetPlayers()) do
player_added(player)
end
The raycasts in this code only hit Terrain now, but what this means is that any Terrain above the player who is in water will result in the code thinking they are on land. Fixing that issue would require a fundamentally different approach to the problem, since raycasts can be configured to ignore water but not normal terrain. If you have any terrain caves, this oxygen system will not work in them.
If you are continuing to have issues beyond this point, I’ll need more insight into your setup as to why things aren’t working–be it variable values at breakpoints or map structure or something–as I’m able to get results on my end minus the conditions each post sets out to remedy.