Hello, so I made a local script placed in the StarterCharacterScripts folder (loaded in the character). For some reasons, there is no problems reading the region where my camera lies and during every runtime the camera’s region voxels are successfully read and the according effects apply. Now, during most runtimes, the game struggles to read the voxel’s at the player’s head position region. It rarely works and returns the 3D table with the right materials but most of the time it only returns the array filled with Enum.Material.Air even if the player region is currently underwater. I should mention that my character always spawns in a region of Enum.Material.Air. I did a series of tests, including expanding the voxel’s region resolution, printing the output of the reading on the client and it still doesn’t work most of the time. Since this problem happens unpredictably depending on the runtime, I assume it has to do something with the fact that it’s a local script but nothing more. At first there were errors thrown in the output about materials[1][1][1] being nil and by the nature of the function ReadVoxels() streaming the data in the array I assume it didn’t have time to load correctly before the script would try to get the data the first times but I’m still a bit confused and it’s been month I tried to figure out why it does that. Also note that before when I first created this script, it would always work perfectly during EVERY runtime and I though maybe Roblox updated their engine so something might have changed.
local character = script.Parent
local terrain = workspace:WaitForChild("Terrain")
local head = character:WaitForChild("Head")
-- Splash
character:WaitForChild("Humanoid").StateChanged:Connect(function(oldState, newState)
if oldState == Enum.HumanoidStateType.Freefall and newState == Enum.HumanoidStateType.Swimming or oldState == Enum.HumanoidStateType.Running and newState == Enum.HumanoidStateType.Swimming then
game.ReplicatedStorage.Remote.AddSplash:FireServer(character.HumanoidRootPart.Position + Vector3.new(0, 1, 0))
end
end)
local underwaterSound = game.SoundService.UnderwaterSound
local tweenService = game:GetService("TweenService")
local underWaterSoundTweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false)
local underwaterSoundFadeIn = tweenService:Create(underwaterSound, underWaterSoundTweenInfo, {Volume = 0.5})
local underwaterSoundFadeOut = tweenService:Create(underwaterSound, underWaterSoundTweenInfo, {Volume = 0})
underwaterSoundFadeOut:Play()
local states = {
isUnderwater = false
}
function checkMaterialAtVector(position)
local expansion = 2
local region = Region3.new(
position - Vector3.new(expansion, expansion, expansion),
position + Vector3.new(expansion, expansion, expansion)
):ExpandToGrid(4)
local voxels = terrain:ReadVoxels(region, 4)
return voxels[1][1][1]
end
function checkPlayer()
local isUnderwater = not (checkMaterialAtVector(head.Position + Vector3.new(0, 2, 0)) == Enum.Material.Air)
if states.isUnderwater ~= isUnderwater then
states.isUnderwater = isUnderwater
if states.isUnderwater then
underwaterSoundFadeIn:Play()
else
underwaterSoundFadeOut:Play()
end
end
if states.isUnderwater ~= character.Data.IsUnderwater.Value then
game.ReplicatedStorage.Remote.ToggleIsUnderwater:FireServer(isUnderwater)
end
end
function checkCamera()
local isInWater = checkMaterialAtVector(workspace.CurrentCamera.CFrame.Position + Vector3.new(0, 2, 0)) == Enum.Material.Water
if isInWater then
game.Lighting.Blur.Enabled = true
else
game.Lighting.Blur.Enabled = false
end
end
while task.wait() do
checkPlayer()
checkCamera()
end
PS: You can remove the splash function and logic and the remote event calls if you want to test on your side (Remotes used for a doublecheck of player’s position on the server to avoid fake data being sent, even if the client still has the control of the player’s position)