I’m making antiexploits atm and ran into a super annoying issue with raycasting. It completely refuses to pick up any sort of water and mistakes me for “flying” in my testing place while swimming in water. IgnoreWater is disabled (by default). I can’t send my entire code here but these are the parameters and the actual raycast itself:
local RaycastParameters = RaycastParams.new()
RaycastParameters.FilterType = Enum.RaycastFilterType.Blacklist
RaycastParameters.FilterDescendantsInstances = {character}
local characterPosition = character:GetPrimaryPartCFrame().Position
local floorRaycast = workspace:Raycast(characterPosition, characterPosition - Vector3.new(0, 10, 0), RaycastParameters)
Tried everything that popped up on other posts about the same issue. Currently my only other option is creating a Region3 around their HumanoidRootPart and reading the voxels, and then detecting if it’s water. I don’t want to go out of my way to do implement this, and would rather find a way for raycasts to work as intended. Doesn’t seem particularly efficient either to create a Region3 considering this is in a .Stepped connection, not 100% sure on this though.
Humanoid:GetState() is not an option here because it’s replicated by the client, and in this instance I’m dealing with exploiters and not regular players.
It’s not officially documented but printing RaycastParams.IgnoreWater without setting it returns false. Setting it to false in the code also results in the system taking action for swimming.
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Blacklist
params.FilterDescendantsInstances = character:GetDescendants()
game:GetService("RunService").RenderStepped:Connect(function()
local origin = workspace.CurrentCamera.CFrame.Position
local result = workspace:Raycast(origin, -Vector3.new(0, 10, 0), params)
print(result)
end)
Seems the cast direction is already relative to the start position of the raycast, this seems to be your issue.
Also, you were right. Not only was the characterPosition subtraction completely unnecessary, it also made the raycast go in the wrong direction. Unfortunately the water ignoring issue still isn’t fixed though.
Upon some further investigation, it seems like it works only when the character is above the water. The same applies to your code. As soon as the camera/character actually enters the water it starts to get ignored.
You can try reading terrain voxels close to the character instead of raycasting and you can see if they’re in the water. A bit performance heavy however.
Raycast only returns the physical geometry of the surface. The moment that ray goes below the surface you are never getting a reading unless you hit the surface below the water.
Edit: So using Terrain:ReadVoxels and getting Material[1][1][1] should be good. I utilize this to figure out if my character is drowning.
For any unfortunate souls who dug this deep to find a decent way to tell if your character is underwater (This is from my module script btw):
Events = {}
function Events:UnderWater(char)
local terrain = workspace.Terrain
local head = char:WaitForChild("Head")
local position = head.Position
local regionSize = 5 -- Size of the region to read
-- region around head
local region = Region3.new(position - Vector3.new(regionSize, regionSize, regionSize),
position + Vector3.new(regionSize, regionSize, regionSize))
-- Read the voxel data in the region
local voxelData = terrain:ReadVoxels(region, 4)
-- Check if any voxel in the region is water
local isUnderwater = false
for x = 1, #voxelData do
for y = 1, #voxelData[x] do
for z = 1, #voxelData[x][y] do
if voxelData[x][y][z] == Enum.Material.Water then
isUnderwater = true
break
end
end
if isUnderwater then break end
end
if isUnderwater then break end
end
if isUnderwater then
-- Character's underwater
return true
end
end
return Events