Here’s my take on an algorithm that does what you need I hope.
As long as you don’t need the exact distance between the target and each of the possible other parts, you can get a very fast distance checking algorithm by leaving out the square root portion of the magnitude formula. Getting the square root is not necessary here because assuming sqrt(a) > sqrt(b) is true then a > b will be true as well. And we know the magnitude is equal to sqrt(x^2 + y^2 + z^2)
, so if we omit the square root from our distance check, then square our minimum distance, we can find objects within a certain radius without using magnitude.
Adding, subtracting, and multiplying is very cheap and it’s all we need for this.
local function distSquared(a: Vector3, b: Vector3)
local dir = b - a
local dX, dY, dZ = dir.X, dir.Y, dir.Z
return dX * dX + dY * dY + dZ * dZ -- magnitude squared
end
local MIN_DISTANCE_STUDS_SQUARED = 50 ^ 2
local target = workspace.target
target.Color = Color3.new(0, 1, 0)
local DEFAULT_COLOUR = BrickColor.new('Medium stone grey').Color
local CLOSEST_COLOUR = Color3.new(1, 0, 1)
local WITHIN_RADIUS_COLOUR = Color3.new(1, 0, 0)
local WITHIN_FOV_COLOUR = Color3.new(0, 1, 1)
local FOV = math.rad(180) -- radians
local otherParts = workspace:WaitForChild('otherParts'):GetChildren()
local otherPartsCount = #otherParts
local lastWithinRadius = {}
while true do
for _,v in lastWithinRadius do
v.Color = DEFAULT_COLOUR
end
table.clear(lastWithinRadius)
local count = 0
for i = 1, otherPartsCount do
local part = otherParts[i]
if distSquared(target.Position, part.Position) < MIN_DISTANCE_STUDS_SQUARED then
otherParts[i].Color = WITHIN_RADIUS_COLOUR
table.insert(lastWithinRadius, otherParts[i])
count += 1
end
end
task.wait()
end
lastWithinRadius
will now be populated with a table of parts within a certain radius.
Now while this is the first piece of the puzzle, we now need to find the parts that are within the enemy’s FOV which I believe is what you meant by “cone shaped”
Unfortunately to my knowledge there’s no great to do this without using division or magnitude since using the dot product requires using unit vectors which is is dividing the vector by its own magnitude. That being said, we’ve already narrowed down the parts that are within the appropriate radius, so we wouldn’t have to query parts that aren’t necessary.
To get whether or not an object is within another’s field of view, what we have to do is get the “look” vector (which is literally target.CFrame.LookVector
) and find the arc cosine of the scalar between it and the directional vector target → query, then determine whether it is smaller than the target field of view * 0.5. If it is, then we know the part is within the field of view.
local withinFOV = table.create(count)
for i = 1, count do
local part = lastWithinRadius[i]
local vec = (part.Position - target.Position).Unit
local look = target.CFrame.LookVector
local dot = vec:Dot(look)
local angle = math.acos(dot)
if angle < 0.5 * FOV then
table.insert(withinFOV, part)
part.Color = WITHIN_FOV_COLOUR
end
end
withinFOV
will now be populated with a list of parts within the other part’s field of view.
Finally, we do one last query on withinFOV
to find the closest part.
local closestDistance, closest = math.huge, nil
for i = 1, withinFOVCount do
local part = withinFOV[i]
local distance = distSquared(part.Position, target.Position)
if distance < closestDistance then
closestDistance = distance
closest = part
end
end
if closest then
closest.Color = CLOSEST_COLOUR
end
And for a demo:
For reference, I was able to run this algorithm 1000 times for 1024 parts in an average of 0.364 seconds.