How Do I Get All The "Neighbours" Of A Part?

I want to get all the Neighbours of a part for example all the parts in green are the Neighbours of the part in red this includes if there are parts above or below it.

how can I get these using scripts. I have tried using raycasting but I could not do it and it seemed performance heavy are there any easier methods? plz help

2 Likes

Just group all of the parts into one group, and group the 4 green parts again, and run a for loop inside the group containing the green parts.

I need to find the green parts by using some method I did that example manually and it is not just one part it is a grid of 625 parts which i need to find the neighbours of

You could loop through all the parts and do this:

if (v.Position - starterBrick.Position).magnitude <= 1 then
    -- do whatever
end

Someone did a test with magnitude and it probably shouldn’t be a performance concern.
Edit: Here is the post that proves it
https://devforum.roblox.com/t/a-lesson-in-over-optimisation-magnitude-calculation/533956

2 Likes

that will depend on the partsize though

I thought of an interesting way of doing this using ray casting and some trigonometry:

local origin = part.Position;

local neighbours = {};
local accuracy = 20;
local rayCastIgnoreGroup = "NeighbourCheck";

-- So the ray casting doesn't detect the origin part
local PhysicsService = game:GetService("PhysicsService");
PhysicsService:CreateCollisionGroup(rayCastIgnoreGroup);
PhysicsService:SetPartCollisionGroup(part, rayCastIgnoreGroup);
PhysicsService:CollisionGroupSetCollidable("Default", rayCastIgnoreGroup, false);

local params = RaycastParams.new();
params.FilterType = Enum.RaycastFilterType.Blacklist;
params.IgnoreWater = true;

--Make a circle around the origin
for i = 0, 1, 1 / accuracy do
	local degree = math.rad(360 * i);
	local scalar = accuracy / 10;

	local x = math.cos(degree) * scalar;
	local z = math.sin(degree) * scalar; 
	
	local getTo = origin + Vector3.new(x, 0, z);
	local dir = (getTo - origin).Unit* 100;

	local result = workspace:Raycast(origin, dir, params);
	if result then
		local neighbour = result.Instance;
		if not neighbours[neighbour] then
			table.insert(neighbours, neighbour);
		end;
	end;
end;

--Color all neighbours green
for _, neighbour in ipairs(neighbours) do
	neighbour.BrickColor = BrickColor.Green();
end;

PhysicsService:SetPartCollisionGroup(part, "Default");

Further research:
I made a tutorial on how to use Trigonometry in Roblox - Help and Feedback / Cool Creations - DevForum | Roblox

3 Likes

You could run :GetTouchingParts() on the part that you want to find its neighbors. This would return a table of all the parts touching it. However, this may return parts diagonally from the part, so I’d recommend checking that each part shares either and X, or Z value with the current part.

Your code would look something like this:

local part = game.Workspace.Part --change this to the part that you want to get it's neighbors
local Touching = part:GetTouchingParts()
local Neighbors = {}

function GetNeighbors()
 for i, Party in pairs(Touching) do
   for i,v in pairs(Neighbors) do
     Neighbors[v] = nil
   end
   if Party.Position.X == Part.Position.X or Party.Position.Y == Part.Position.Y then
      if Party.Position.Y == Part.Position.Y then
         Neighbors.insert(Party)
         print(v.Name.." is a neighbor!")
         --The part is a neighbor & it has been added to the grand table of neighbors
      end
   end
 end
end

GetNeighbors()

You can then loop through the ‘Neighbors’ table using a for loop to get all the neighbors of the part!

Anyways, I hope this helped, if it did, be sure to mark it as a solution so I can get my third solution, and so people having this problem can find the answer easier!

3 Likes

get touching parts does not work

Updated the code, try now?
Char limit!!!

The easiest way is to keep track of it in the first place.

How are you creating this grid of parts?

If you generate it with a script, finding neighbors becomes easy!

I didn’t test, but something like this:

local parts = {}

local rows = 10
local cols = 10

-- generate a grid and put it in the parts table
for row = 1, rows do
	parts[row] = {}
	for col = 1, cols do
		local p = Instance.new("Part")
		p.Size = Vector3.new(1,1,1)
		p.Position = Vector3.new(col, 0, row)
		p.Anchored = true
		p.Parent = workspace
		parts[row][col] = p
	end
end

local function GetNeighbors(row, col)
	local neighbors = {}

	if col > 1 then neighbors["left"] = parts[row][col-1] end
	if col < cols then neighbors["right"] = parts[row][col+1] end
	if row > 1 then neighbors["top"] = parts[row-1][col] end
	if row < rows then neighbors["bottom"] = parts[row+1][col] end

	return neighbors
end


-- for example, get the neighbors of the part at row 5, column 3:

local neighbors = GetNeighbors(5, 3)

neighbors.left.Color = Color3.new(1,0,0)
neighbors.top.Color = Color3.new(0,1,0)
neighbors.right.Color = Color3.new(0,0,1)
neighbors.bottom.Color = Color3.new(1,1,0)
3 Likes

What you could do instead also is u can raycast the inverse look vector and the look vector and the right vector and the inverse right vector then see if the intersection distance is the size + size.X*0.5 + 0.01

The 0.01 is just a epsilon cause of float errors.

1 Like

You should also know that if you are trying to get the neighbors cause u trying to do cellular automata

you are much better off for looping those parts into a matrix then doing some matrix math (would be a different devforum post). Ideally we’d run this on the gpu as a vertex shader but you could probably get away with parallel luau since it looks like your scene is small.

(had to make a new post cause devforum bug)

Just raycast on all 6 sides:

local Position = -- Red part
local Up = -- Raycast from Position + Vector3.new(0, 1, 0)
local Down = -- Raycast from Position + Vector3.new(0, -1, 0)
local Left = -- Raycast from Position + Vector3.new(-1, 0, 0)
local Right = -- Raycast from Position + Vector3.new(1, 0, 0)
local Front = -- Raycast from Position + Vector3.new(0, 0, 1)
local Back = -- Raycast from Position + Vector3.new(0, 0, -1)

Raycasts look like this:

local Position = -- Red part
local RayParams = RaycastParams.new()

local Up = workspace:Raycast(Position, Vector3.new(0, 1, 0), RayParams)

This would not work as i am creating the parts in a for loop

@Q_ubit and @Cryptic_spike gave you solutions or for you an idea. Could you make it as solution?

2 Likes

My first @! :partying_face:
Char limit thing :frowning_face:

I am creating the parts in a for loop e.g

for i = 1, 100 do
   for i = 1, 100 do

Did you see my post? I specifically show you how to do it if you’re making the parts in a loop.

Just run it trough 2 for loops and check if if the cell we are checking isn’t the same cell

u can actualy use find parts in region might be more efficient then itteration over all parts in a folder or the map if its is a huge map and then check the position is close

and then check if the part in the region is redpart.Position.X - partSize.X etc u get the point i think
(assuming part is 1x1x1) stud size
example

local partSize = 1 -- 1 stud change if bigger
local redPart = game.workspace.map.RedPart
local Point1 = Vector3.new((RedPart.Position.X - RedPart.Size.X/2) - partSize,0,(RedPart.Position.Z- RedPart.Size.Z/2) -partSize) -- top left point1 1 stud wider then redpart position

local Point1 = Vector3.new((RedPart.Position.X + RedPart.Size.X/2) + partSize,0,(RedPart.Position.Z+ RedPart.Size.Z/2) +partSize) -- bottom right point2 1 stud wider then redpart position

local Region = Region3.new(Point1,Point2)
local parts = workspace:FindPartsInRegion3(region, redPart) -- ignore redPart

– this return all parts arround the redpart like this X is redpart
[ ][B][ ]
[L][X][R]
[ ][F][ ]
we still have 4 parts too much so we need to filter a bit more specific by checking if part is:

  • in front (Z-) [F]
  • in back (Z+) [B]
  • left (X-) [L]
  • right (X+) [F]

so we do

for _,Part in pairs(parts) do
if Part.Position == Vector3.new((redPart.Position.X-redPart.Size.X/2)-Partsize/2,redPart.Position.Y,redPart.Position.Z) then
-- left part
elseif Part.Position == Vector3.new((redPart.Position.X+redPart.Size.X/2)+Partsize/2,redPart.Position.Y,redPart.Position.Z) then
--right part
elseif Part.Position == Vector3.new(redPart.Position.X,redPart.Position.Y,(redPart.Position.Z-redPart.Size.Z/2)-partSize/2) then
--front part
elseif Part.Position == Vector3.new(redPart.Position.X,redPart.Position.Y,(redPart.Position.Z+redPart.Size.Z/2)+partSize/2) then
--backpart
end
print(Part.Name)
end

i did not do above or below but its same princable just check y+ y- and also dont forget to change y+ y- on point1 and point2 to include it in the 3d region select

1 Like