 # Infinite tile generation around player

Hello

I am trying to create infinite tiles a player can walk over.
Some keypoints:

• 9 tiles must always be around the character.
• The tiles need to stay stationary, they can only change their position on creation.
• Tiles that don’t need to be recreated need to be reused.

I’m having trouble figuring out whenever the parts need to get updated. I currently have a threshold if a player walks to far from the center, it finds the closest part and builds 9 new parts around that closest part.
However, this can lead to flickering if the code doesn’t exactly find the closest part.

The reusing of tiles is also difficult for me.

Here's my (messy) code:
``````local floatingPointCorrection = 0.25
local partTable = {}
local updateInProgress = false
-- Standard configuration (indexes of table):
-- 1 = TopLeft
-- 2 = TopMiddle
-- 3 = TopRight
-- 4 = MiddleLeft
-- 5 = MiddleMiddle (reference)
-- 6 = MiddleRight
-- 7 = BottomLeft
-- 8 = BottomMiddle
-- 9 = BottomRight

-- Compute positions around referencePosition (only for squares)
local function positionsAroundReference(refPos, length)
return {
 = refPos + Vector3.new(-length, 0, length),
 = refPos + Vector3.new(0, 0, length),
 = refPos + Vector3.new(length, 0, length),
 = refPos + Vector3.new(-length, 0, 0),
 = refPos,
 = refPos + Vector3.new(length, 0, 0),
 = refPos + Vector3.new(-length, 0, -length),
 = refPos + Vector3.new(0, 0, -length),
 = refPos + Vector3.new(length, 0, -length),
}
end

-- Create a part at the specified position
local function createPart(pos)
local part = Instance.new("Part")
part.Size = Vector3.new(25, 1, 25)
part.Anchored = true
part.CanCollide = true
part.Position = pos
part.Parent = workspace
return part
end

local function cloneTable(src)
local newTable = {}
for i, v in pairs(src) do
newTable[i] = v
end
return newTable
end

local function updatePartTable(sourcePart)
if updateInProgress then
return
end
print("Update")
updateInProgress = true

for _, v in pairs(partTable) do
v:Destroy()
end
if sourcePart then
sourcePart:Destroy()
end

for i, newPosition in pairs(positionsAroundReference(sourcePart.Position, sourcePart.Size.X)) do
partTable[i] = createPart(newPosition)
end

-- local newTable = {}
-- for i, newPosition in pairs(positionsAroundReference(sourcePart.Position, sourcePart.Size.X)) do
-- 	-- Check if other already created part has the same position
-- 	for _, v in pairs(partTable) do
-- 		if (v.Position - newPosition).Magnitude <= floatingPointCorrection then
-- 			table.insert(newTable, i, v)
-- 		end
-- 	end
-- 	-- Create a new part
-- 	table.insert(newTable, i, createPart(newPosition))
-- end

-- -- Remove unused parts
-- for _, oldPart in pairs(partTable) do
-- 	local found = table.find(newTable, oldPart)
-- 		print("Destroy")
-- 		oldPart:Destroy()
-- 	end
-- end
-- partTable = cloneTable(newTable)

updateInProgress = false
end

local sourcePart = createPart(Vector3.new(0, 200, 0))

updatePartTable(sourcePart)

game:GetService("RunService").Heartbeat:Connect(function()
local char = game:GetService("Players").LocalPlayer.Character
if char then
local rootPart = char:FindFirstChild("HumanoidRootPart")
if rootPart and partTable then
-- Vector pointing from middle part to HumanoidRootPart (Vector2)
local rootVector = Vector2.new(rootPart.Position.X, rootPart.Position.Z)
- Vector2.new(partTable.Position.X, partTable.Position.Z)
local distance = rootVector.Magnitude

if distance > (partTable.Size.X * (2 / 3)) then
-- Player has walked further than max distance --> update parts
-- local closestPart
-- local closestDistance = partTable.Size.X * 2
-- for i, part in pairs(partTable) do
-- 	if i ~= 5 then -- Don't use middle part
-- 		local newDistance =
-- 			(Vector2.new(part.Position.X, part.Position.Z) - Vector2.new(rootPart.Position.X, rootPart.Position.Z)).Magnitude
-- 		if newDistance < closestDistance + (part.Size.X / 12) then -- If part is closer (by a margin)
-- 			closestPart = part
-- 			closestDistance = newDistance
-- 		end
-- 	end
-- end
-- updatePartTable(closestPart)

-- Get part --> HumanoidRootPart vector with the least angle (get part player is moving towards)
local directionPart
local smallestAngle = math.huge
for i, part in pairs(partTable) do
if i ~= 5 then -- Don't use middle part
-- |v1||v2|cos(a) = v1*v2
-- a=acos((v1*v2)/(|v1||v2|))
local distVector =
(
Vector2.new(part.Position.X, part.Position.Z)
- Vector2.new(rootPart.Position.X, rootPart.Position.Z)
)
local angle =
math.acos(
rootVector:Dot(distVector) / (rootVector.Magnitude * distVector.Magnitude)
)
-- Always get sharp angle
if angle > 90 then
angle = 180 - angle
end
-- Check if smaller
if math.abs(angle) < smallestAngle then
smallestAngle = angle
directionPart = part
end
end
end
updatePartTable(directionPart)
end
end
end
end)

``````
1 Like

Sorry for the bump, but does anyone have an idea on this? I’m still stuck .

Notice that it moves when you’re off the middle

All I did was add a cooldown so you can see

``````local cooldown = false
local function updatePartTable(sourcePart)
if updateInProgress or cooldown then
return
end
cooldown = true
delay(0.5, function()
cooldown = false
end)
``````

The flickering back and forth you see at around 6 seconds in the video you provided is what I want to prevent. I want it to change the parts as little as possible, whilst still achieving an infinite effect.

So I got the reusing of parts working but I can’t seem to fix the flickering issue. Whenever you walk exactly to the corner, the script gets confused about what part is the closest and jumps back and forth very quickly. Causing a lag spike.

Here’s my code now:

``````local floatingPointCorrection = 0.25
local debounceCorrection = 5
local partTable = {}
local updateInProgress = false
-- Standard configuration (indexes of table):
-- 1 = TopLeft
-- 2 = TopMiddle
-- 3 = TopRight
-- 4 = MiddleLeft
-- 5 = MiddleMiddle (reference)
-- 6 = MiddleRight
-- 7 = BottomLeft
-- 8 = BottomMiddle
-- 9 = BottomRight

-- Compute positions around referencePosition (only for squares)
local function positionsAroundReference(refPart)
local refPos = refPart.Position
local length = refPart.Size.X
return {
 = refPos + Vector3.new(-length, 0, length),
 = refPos + Vector3.new(0, 0, length),
 = refPos + Vector3.new(length, 0, length),
 = refPos + Vector3.new(-length, 0, 0),
 = refPos,
 = refPos + Vector3.new(length, 0, 0),
 = refPos + Vector3.new(-length, 0, -length),
 = refPos + Vector3.new(0, 0, -length),
 = refPos + Vector3.new(length, 0, -length),
}
end

-- Create a part at the specified position
local function createPart(pos)
local part = Instance.new("Part")
part.Size = Vector3.new(25, 1, 25)
part.Anchored = true
part.CanCollide = true
part.Position = pos
part.Parent = workspace

-- local part = workspace.Ocean.Plane:Clone()
-- part.Position += Vector3.new(0, 200, 0)
-- part.Parent = workspace
return part
end

local function updatePartTable(sourcePart)
if updateInProgress then
return
end
print("Update")
updateInProgress = true

-- Get new positions
local newPositions = positionsAroundReference(sourcePart)

local newTable = {}
for index, newPos in pairs(newPositions) do
if index == 5 then
-- Use sourcePart in the middle
newTable[index] = sourcePart
else
for _, part in pairs(partTable) do
if (part.Position - newPos).Magnitude <= floatingPointCorrection then
-- This already created part has the position we want!
newTable[index] = part
end
end

if not newTable[index] then
-- Create a new part
newTable[index] = createPart(newPos)
end
end
end

-- Remove unused parts
for _, v in pairs(partTable) do
local found = table.find(newTable, v)
v:Destroy()
end
end

-- Update partsTable
for i, v in pairs(newTable) do
partTable[i] = v
end

updateInProgress = false
end

local sourcePart = createPart(Vector3.new(0, 200, 0))

updatePartTable(sourcePart)

game:GetService("RunService").Heartbeat:Connect(function()
local char = game:GetService("Players").LocalPlayer.Character
if char then
local rootPart = char:FindFirstChild("HumanoidRootPart")
if rootPart and partTable then
-- Vector pointing from middle part to HumanoidRootPart (Vector2)
local rootVector = Vector2.new(rootPart.Position.X, rootPart.Position.Z)
- Vector2.new(partTable.Position.X, partTable.Position.Z)
local distance = rootVector.Magnitude

if distance > (partTable.Size.X * (2 / 3)) then
-- Player has walked further than max distance --> update parts
local closestPart
local closestDistance = partTable.Size.X * 2
for i, part in pairs(partTable) do
if i ~= 5 then -- Don't use middle part
local newDistance =
(Vector2.new(part.Position.X, part.Position.Z) - Vector2.new(rootPart.Position.X, rootPart.Position.Z)).Magnitude
if newDistance + debounceCorrection < closestDistance then -- If part is closer (by a margin)
closestPart = part
closestDistance = newDistance
end
end
end
updatePartTable(closestPart)
end
end
end
end)

``````