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 {
[1] = refPos + Vector3.new(-length, 0, length),
[2] = refPos + Vector3.new(0, 0, length),
[3] = refPos + Vector3.new(length, 0, length),
[4] = refPos + Vector3.new(-length, 0, 0),
[5] = refPos,
[6] = refPos + Vector3.new(length, 0, 0),
[7] = refPos + Vector3.new(-length, 0, -length),
[8] = refPos + Vector3.new(0, 0, -length),
[9] = 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
-- print("Use already created part")
-- table.insert(newTable, i, v)
-- continue -- Jump to next iteration
-- 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)
-- if not found then
-- 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[5] then
-- Vector pointing from middle part to HumanoidRootPart (Vector2)
local rootVector = Vector2.new(rootPart.Position.X, rootPart.Position.Z)
- Vector2.new(partTable[5].Position.X, partTable[5].Position.Z)
local distance = rootVector.Magnitude
if distance > (partTable[5].Size.X * (2 / 3)) then
-- Player has walked further than max distance --> update parts
-- local closestPart
-- local closestDistance = partTable[5].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)