I was able to significantly thin down the number of raycasts I was doing (before there were several that were straight-up unnecessary), and now I’m able to remove all task.wait()
s in the entire script if I’m scanning at 256 and the whole process takes about 8.5 seconds. It still times out at 512, and above, so here is the check()
function I use:
-- function to check whether a node is colliding or not
local function check(node, position, size)
-- checking parts collisions
local colliding = #workspace:GetPartsInPart(node, paramsOL) > 0
if colliding == true then return colliding end
-- checking terrain collisions (>= 4x4x4)
if size >= 4 then
local materials, occupancies = workspace.Terrain:ReadVoxels(Region3.new(position - (Vector3.one * size * 0.5), position + (Vector3.one * size * 0.5)), 4)
for _, x in materials do
if _ == "Size" then continue end
for _, y in x do
for _, z in y do
if z ~= Enum.Material.Air then colliding = true return colliding end
end
end
end
end
-- checking terrain collisions
colliding = cubecast(position, size) ~= nil or centercast(position, size) ~= nil or node.Size.Y > 2 and perimeteranddiagonalcast(node.Position, node.Size.Y) ~= nil
return colliding
end
The first test uses workspace:GetPartsInPart()
to scan for parts. If nothing comes up, it moves to the next test which uses workspace.Terrain:ReadVoxels()
to scan for terrain in all nodes 4 or larger. If both those tests fail, the last test all nodes go through is to execute two custom raycasting functions cubecast()
and centercast()
, with nodes 2 or smaller executing a third function if those fail called perimeteranddiagonalcast()
. All three custom functions are listed below.
cubecast()
:
-- completes blockcasts through the center
local function cubecast(position, size)
local result
local posL = CFrame.new(position + Vector3.new(size * 0.45, 0, 0))
local posR = CFrame.new(position - Vector3.new(size * 0.45, 0, 0))
local posU = CFrame.new(position + Vector3.new(0, size * 0.45, 0))
local posD = CFrame.new(position - Vector3.new(0, size * 0.45, 0))
local posF = CFrame.new(position + Vector3.new(0, 0, size * 0.45))
local posB = CFrame.new(position - Vector3.new(0, 0, size * 0.45))
local desL = CFrame.new(position - Vector3.new(size * 0.4, 0, 0))
local desR = CFrame.new(position + Vector3.new(size * 0.4, 0, 0))
local desU = CFrame.new(position - Vector3.new(0, size * 0.4, 0))
local desD = CFrame.new(position + Vector3.new(0, size * 0.4, 0))
local desF = CFrame.new(position - Vector3.new(0, 0, size * 0.4))
local desB = CFrame.new(position + Vector3.new(0, 0, size * 0.4))
local sizeX = Vector3.new(size * 0.1, size * 0.9, size * 0.9)
local sizeY = Vector3.new(size * 0.9, size * 0.1, size * 0.9)
local sizeZ = Vector3.new(size * 0.9, size * 0.9, size * 0.1)
result = workspace:Blockcast(posL, sizeX, desL.Position - posL.Position, paramsRC) if result ~= nil then return result end
result = workspace:Blockcast(posR, sizeX, desR.Position - posR.Position, paramsRC) if result ~= nil then return result end
result = workspace:Blockcast(posU, sizeY, desU.Position - posU.Position, paramsRC) if result ~= nil then return result end
result = workspace:Blockcast(posD, sizeY, desD.Position - posD.Position, paramsRC) if result ~= nil then return result end
result = workspace:Blockcast(posF, sizeZ, desF.Position - posF.Position, paramsRC) if result ~= nil then return result end
result = workspace:Blockcast(posB, sizeZ, desB.Position - posB.Position, paramsRC) if result ~= nil then return result end
return result
end
centercast()
:
-- completes raycasts through the center
local function centercast(position, size)
local result
local posL = position + Vector3.new(size * 0.5, 0, 0)
local posR = position - Vector3.new(size * 0.5, 0, 0)
local posU = position + Vector3.new(0, size * 0.5, 0)
local posD = position - Vector3.new(0, size * 0.5, 0)
local posF = position + Vector3.new(0, 0, size * 0.5)
local posB = position - Vector3.new(0, 0, size * 0.5)
local desL = position - Vector3.new(size * 0.4, 0, 0)
local desR = position + Vector3.new(size * 0.4, 0, 0)
local desU = position - Vector3.new(0, size * 0.4, 0)
local desD = position + Vector3.new(0, size * 0.4, 0)
local desF = position - Vector3.new(0, 0, size * 0.4)
local desB = position + Vector3.new(0, 0, size * 0.4)
result = workspace:Raycast(posL, desL - posL, paramsRC) if result ~= nil then return result end
result = workspace:Raycast(posR, desR - posR, paramsRC) if result ~= nil then return result end
result = workspace:Raycast(posU, desU - posU, paramsRC) if result ~= nil then return result end
result = workspace:Raycast(posD, desD - posD, paramsRC) if result ~= nil then return result end
result = workspace:Raycast(posF, desF - posF, paramsRC) if result ~= nil then return result end
result = workspace:Raycast(posB, desB - posB, paramsRC) if result ~= nil then return result end
return result
end
perimeteranddiagonalcast()
:
-- completes raycasts across the edges and through the diagonals
local function perimeteranddiagonalcast(position, size)
local result
local corners = {
position + Vector3.new(size * 0.5, size * 0.5, size * 0.5),
position + Vector3.new(size * 0.5, size * 0.5, -size * 0.5),
position + Vector3.new(size * 0.5, -size * 0.5, size * 0.5),
position + Vector3.new(size * 0.5, -size * 0.5, -size * 0.5),
position + Vector3.new(-size * 0.5, size * 0.5, size * 0.5),
position + Vector3.new(-size * 0.5, size * 0.5, -size * 0.5),
position + Vector3.new(-size * 0.5, -size * 0.5, size * 0.5),
position + Vector3.new(-size * 0.5, -size * 0.5, -size * 0.5)
}
for i = 1, 8 do
for j = 1, 8 do
if i == j then continue end
result = workspace:Raycast(corners[i], corners[j] - corners[i], paramsRC) if result ~= nil then return result end
end
end
return result
end
The first two functions don’t go all the way through to the other side to prevent false positives from hitting something that isn’t contained by the node but merely touches up against it. The first two raycast tests run for all size nodes because if they don’t, it leaves massive holes in the grid that I mentioned in an earlier post (they look small in the photos but each node is size 16 or 32, I believe):
Admittedly, I was initially trying to make perimeteranddiagonalcast()
into just perimetercast()
but I got confused trying to write out what connections constituted the edges of the cube, got lazy, and just decided to test all vertices against all other verticles.
Finally, here’s some data I gathered about running different combinations of functions (scanning 256):
(perimeteranddiagonalcast for <= 2x2x2)
cubecast + centercast: 8.19 sec, 32382 nodes
cubecast: 5.86 sec, 31580 nodes
centercast: 6.08 sec, 26357 nodes
neither (perimeteranddiagonalcast for everything): DNF, 36203 nodes (w/ task.wait())