Finding the closest object

Hello! I am looking for a more efficient method for getting the nearest object to the Core BasePart. I wrote this below but I have a feeling that it is very excessive. Let me know what you think.

local Model = workspace.Model
local Core = workspace.Core

local NearbyChild
local MaxSearchDistance = 30
local SearchDistance = 1

while wait(1) do
	if not NearbyChild then
		for Index,Child in pairs(Model:GetChildren()) do
			if (Child.Position - Core.Position).Magnitude < SearchDistance then
				NearbyChild = Child
			end
		end
		if SearchDistance < MaxSearchDistance then
			SearchDistance = SearchDistance + 1
		end
	end
	print(NearbyChild,SearchDistance)
end


4 Likes

Region3 is cool and I thought about using it.

How else could I go about comparing and getting the shortest distance?

I was looking for a more effective way to get that shortest distance. I wasn’t looking for a better method to get the children in range as that doesn’t matter to me right now. Thank you for your help anyway! I like your beard.

Here is another method I just came up with! I would appreciate any input and advice from anyone!

local Model = workspace.Model
local Core = workspace.Core

function GetDistanceFromChildren()
	local Position_Contain = {}
	for Index,Child in pairs(Model:GetChildren()) do
		table.insert(Position_Contain,Index,(Child.Position - Core.Position).Magnitude)
	end
	for Index,Child in pairs(Model:GetChildren()) do
		if (Core.Position - Child.Position).Magnitude == math.min(unpack(Position_Contain)) then
			print(Child)
		end
	end
end

GetDistanceFromChildren()
2 Likes

Hello, I was wondering about this earlier. Myself and others came to the conclusion that you should avoid calculating region3s rapidly. This is very inefficient. Instead, depending on how many children you have to look for, just keep doing what you’re doing. You can read more about different options on my posts comments.

1 Like

This new method I just posted works really well. I’d still like to find another way.

Would there be interest in a KD-tree implementation in Roblox for efficient nearest neighbor queries?

I might whip one up if people are super interested in it.

(Note: KD-Trees only really work if you have static points that you want to check against. In this case, if your model is moving, KD-trees wouldn’t work)

3 Likes

The 2nd method you posted seems to be fairly optimized. I could recommend a change though. I dont know how many parts are in your model, but you could save time by just getting the primary parts CFrame instead of iterating through all the children and saving there positions. This would mean you only have 2 parts to determine the positions of. This is what I did when I was working on a zombie game a while back. I just checked if the character and the zombies HumanoidRootParts were close to each other.

Other than that your code seems fairly optimized :slight_smile:

I made a modified version. I dont know if this is what youre aiming for but I tried XD

local ModelPosition = workspace.Model:GetPrimaryPartCFrame().Position
local CorePosition = workspace.Core:GetPrimaryPartCFrame().Position

function GetDistanceFromObjects()
 	if (ModelPosition - CorePosition).Magnitude < 25 then
		print("Object is close?")
	end
end

while wait(.75) do
	GetDistanceFromObjects()
end
2 Likes

That’s a much better improvement than your original code, however, there are certain things which you are doing every iteration within your for loop that need not be repeated.

	for Index,Child in pairs(Model:GetChildren()) do
		if (Core.Position - Child.Position).Magnitude == math.min(unpack(Position_Contain)) then
			print(Child)
		end
	end

Every iteration of that for loop, math.min(unpack(Position_Contain)) is re-calculated. You could’ve calculated it before the 2nd for loop began and accessed it via variable as it only needs to be calculated once and doesn’t change throughout the second for loop. Also, unless the children in the model constantly change, you can call Model:GetChildren() outside of any loop for an improvement instead of using that method to get the same table every time.
Also, you can add a break statement to your for loop once it has found the closest object which signals it from iterating further (as the closest object has already been found).

The goal here is to minimise the number of operations per iteration and reduce the iteration count.
How I would do it is:

  1. Get and store a variable containing the children of Model and iterate over it’s contents.
  2. Have a “running” variable which updates throughout the course of a single for loop. This variable will represent the minimum distance found so far. Use a second variable to represent the object from which that distance was calculated.
  3. Once all children of the Model have been checked, return the closest object found.
local Model = workspace.Model
local Core = workspace.Core

local Contents = Model:GetChildren()
local MaxDistance = 30


function GetDistanceFromChildren()
    local MinDistance = MaxDistance --Start off assuming the min. dist is the max and try to find smaller
    local ClosestObject

    for Index, Child in ipairs(Contents) do
        local dist = (Child.Position - Core.Position).Magnitude --Prevent's calculating the distance twice per iter
        if dist < MinDistance then
            MinDistance, ClosestObject = dist, Child
        end
    end
    --Has to check all objects without breaking as next object in next iteration could be closer
    return ClosestObject
end

local Closest = GetDistanceFromChildren()

Overall, this has halved the number of iterations from your previous code.

5 Likes

your implementation is fine assuming there arent thousands of parts in the model.

vesteria iterates through up to hundreds of parts and does magnitude checks every heartbeat and its more than fine.

why do you need “a more efficient method” ? what is your use case? its more probable you can tailor a solution to your specific need rather than looking for a one-size-fits-all solution.

2 Likes

To figure out if you even need to optimize this, check out how to use the MicroProfiler.