How do I move the world around the player for a terrain system with minimal lag?

Hello, Developer Community!

I’m currently creating a space game where you can visit life-size planets, land, build, etc. However, I’ve come across a key issue in my attempt to create a “the world moves around the player” system: lag!

No matter what I do, I cannot seem to find a balance between seamlessness, performance, and visual appeal. I know it’s possible because I’ve seen many others do it. I spent the last 2 days playing around with code. Giving up, I decided to default back to baseline functionality.

(Uses PartCache)

Terrain.lua

local Chunk = require(game.ReplicatedStorage.Chunk)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local RENDER_DISTANCE = 10
local CHUNKS_LOADED_PER_TICK = 4
local CAMERA = workspace.CurrentCamera
local MinFPS = 30
local performanceMeter = require(ReplicatedStorage:WaitForChild("ClientPerformanceMeter")).new({ fpsThreshold = MinFPS })

local centerPosX, centerPosZ
local chunks = {}
local chunkLoadCounter = 0
local fastLoad = true
local localPlayer = Players.LocalPlayer

local function getChunkKey(x, z)
	return x .. "," .. z
end

local function chunkWait()
	chunkLoadCounter = (chunkLoadCounter + 1) % CHUNKS_LOADED_PER_TICK
	if chunkLoadCounter == 0 and not fastLoad then
		task.wait()
	end
end

local function updateChunks()
	local playerPos = localPlayer.Character.PrimaryPart.Position
	centerPosX = math.floor(playerPos.x / Chunk.WIDTH_SIZE_X)
	centerPosZ = math.floor(playerPos.z / Chunk.WIDTH_SIZE_Z)

	local requiredChunks = {}
	for x = centerPosX - RENDER_DISTANCE, centerPosX + RENDER_DISTANCE do
		for z = centerPosZ - RENDER_DISTANCE, centerPosZ + RENDER_DISTANCE do
			local key = getChunkKey(x, z)
			requiredChunks[key] = true
		end
	end

	for key, chunk in pairs(chunks) do
		if not requiredChunks[key] then
			chunk:Destroy(fastLoad)
			chunks[key] = nil
			chunkWait()
		end
	end

	for key, _ in pairs(requiredChunks) do
		if not chunks[key] then
			local parts = key:split(",")
			local x = tonumber(parts[1])
			local z = tonumber(parts[2])
			chunks[key] = Chunk.new(x, z, fastLoad)
			chunkWait()
		end
	end
end

local lastAdjustmentTime = 0
local DEBOUNCE_TIME = 5
performanceMeter:OnLowFPS(function(currentFPS)
	local now = tick()
	if now - lastAdjustmentTime >= DEBOUNCE_TIME then
		if RENDER_DISTANCE > 1 then
			RENDER_DISTANCE = math.max(1, RENDER_DISTANCE - 1)
			warn("Lag detected, reduced render distance to " .. tostring(RENDER_DISTANCE))
		end
		lastAdjustmentTime = now
	end
end)

while true do
	local character = localPlayer.Character
	if character and character.PrimaryPart then
		updateChunks()
		if fastLoad then
			print("Initial chunk load complete. Switching to optimized loading.")
			fastLoad = false
		end
	end
	task.wait(0.5)
end

Chunk.lua

local RS = game:GetService("ReplicatedStorage")

local Biomes = require(RS.Biomes)
local pc = require(RS.PartCache)

local X, Z = 5, 5
local WIDTH_SCALE = 25
local WATER_FLOOR_Y = -200

local wedge = RS:WaitForChild("Wedge")
wedge.Anchored = true
wedge.TopSurface = Enum.SurfaceType.Smooth
wedge.BottomSurface = Enum.SurfaceType.Smooth
wedge.CanTouch = false
wedge.CanQuery = false
wedge.CollisionGroup = "WedgeGroup"

local returnBuffer = RS.ReturnBuffer
local cache = RS.Cache

local PartCache = pc.new(wedge, 200000, cache)


local function lerp(a, b, t)
	return a + (b - a) * t
end

local function getColorFromPalette(wedgeHeight, TERRAIN_HEIGHT_COLORS)
	local lowerColorHeight, higherColorHeight
	for height, heightColor in pairs(TERRAIN_HEIGHT_COLORS) do
		if wedgeHeight == height then return heightColor end
		if (wedgeHeight < height) and (not higherColorHeight or height < higherColorHeight) then
			higherColorHeight = height
		end
		if (wedgeHeight > height) and (not lowerColorHeight or height > lowerColorHeight) then
			lowerColorHeight = height
		end
	end
	if not lowerColorHeight then return TERRAIN_HEIGHT_COLORS[higherColorHeight] end
	if not higherColorHeight then return TERRAIN_HEIGHT_COLORS[lowerColorHeight] end
	local alpha = (wedgeHeight - lowerColorHeight) / (higherColorHeight - lowerColorHeight)
	return TERRAIN_HEIGHT_COLORS[lowerColorHeight]:lerp(TERRAIN_HEIGHT_COLORS[higherColorHeight], alpha)
end

local function draw3dTriangle(a, b, c)
	local ab, ac, bc = b - a, c - a, c - b;
	local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc);
	if (abd > acd and abd > bcd) then c, a = a, c; elseif (acd > bcd and acd > abd) then a, b = b, a; end
	ab, ac, bc = b - a, c - a, c - b;
	local right = ac:Cross(ab).unit;
	local up = bc:Cross(right).unit;
	local back = bc.unit;
	local height = math.abs(ab:Dot(up));
	local w1 = PartCache:GetPart()
	w1.Size = Vector3.new(0, height, math.abs(ab:Dot(back)))
	w1.CFrame = CFrame.fromMatrix((a + b)/2, right, up, back)
	local w2 = PartCache:GetPart()
	w2.Size = Vector3.new(0, height, math.abs(ac:Dot(back)))
	w2.CFrame = CFrame.fromMatrix((a + c)/2, -right, up, -back)
	return w1, w2
end

local function getHeight(chunkPosX, chunkPosZ, x, z, biomeA, biomeB, alpha)
	local smoothness = lerp(biomeA.TerrainSmoothness, biomeB.TerrainSmoothness, alpha)
	local heightScale = lerp(biomeA.HeightScale, biomeB.HeightScale, alpha)
	local noiseValue = math.noise(
		(X / smoothness * chunkPosX) + x / smoothness,
		(Z / smoothness * chunkPosZ) + z / smoothness
	)
	local heightA = biomeA.ApplyHeightAmplification(noiseValue * biomeA.HeightScale)
	local heightB = biomeB.ApplyHeightAmplification(noiseValue * biomeB.HeightScale)
	return lerp(heightA, heightB, alpha)
end

local function getPosition(chunkPosX, chunkPosZ, x, z)
	local worldX = chunkPosX*X*WIDTH_SCALE + x*WIDTH_SCALE
	local worldZ = chunkPosZ*Z*WIDTH_SCALE + z*WIDTH_SCALE

	local biomeA, biomeB, alpha = Biomes.getBiomeData(worldX, worldZ)
	local worldY = getHeight(chunkPosX, chunkPosZ, x, z, biomeA, biomeB, alpha)

	return Vector3.new(
		worldX,
		worldY,
		worldZ
	)
end

local function paintWedge(wedge)
	local wedgePos = wedge.Position
	local biomeA, biomeB, alpha = Biomes.getBiomeData(wedgePos.X, wedgePos.Z)
	local colorA = getColorFromPalette(wedgePos.Y, biomeA.HeightColors)
	local colorB = getColorFromPalette(wedgePos.Y, biomeB.HeightColors)
	wedge.Color = colorA:lerp(colorB, alpha)
	wedge.Material = Enum.Material.Grass
end

local function addWater(chunk)
	local chunkCenterX = (chunk.x + 0.5) * chunk.WIDTH_SIZE_X
	local chunkCenterZ = (chunk.z + 0.5) * chunk.WIDTH_SIZE_Z
	local biomeA, biomeB, alpha = Biomes.getBiomeData(chunkCenterX, chunkCenterZ)
	local dominantBiome = if alpha > 0.5 then biomeB else biomeA

	if not dominantBiome.HasWater then return end
	local waterTopY = dominantBiome.WaterLevel
	local waterHeight = waterTopY - WATER_FLOOR_Y
	if waterHeight <= 0 then return end

	local waterPart = Instance.new("Part")
	waterPart.Anchored = true
	waterPart.CanCollide = false
	waterPart.CanTouch = false
	waterPart.CanQuery = false
	waterPart.Size = Vector3.new(chunk.WIDTH_SIZE_X, waterHeight, chunk.WIDTH_SIZE_Z)
	waterPart.CFrame = CFrame.new(chunkCenterX, (waterTopY + WATER_FLOOR_Y) / 2, chunkCenterZ)
	waterPart.Color = Color3.fromRGB(0, 175, 255)
	waterPart.Transparency = 0.5
	waterPart.Material = Enum.Material.Glass
	waterPart.Name = "Water"

	chunk.waterPart = waterPart
end

local Chunk = {}
Chunk.__index = Chunk
Chunk.WIDTH_SIZE_X = X * WIDTH_SCALE
Chunk.WIDTH_SIZE_Z = Z * WIDTH_SCALE

function Chunk.new(chunkPosX, chunkPosZ, fastload)
	local chunk = { x = chunkPosX, z = chunkPosZ }
	setmetatable(chunk, Chunk)

	local chunkModel = Instance.new("Model")
	chunkModel.Name = "Chunk_"..chunkPosX.."_"..chunkPosZ

	local positionGrid = {}
	for x = 0, X do
		positionGrid[x] = {}
		for z = 0, Z do
			positionGrid[x][z] = getPosition(chunkPosX, chunkPosZ, x, z)
		end
	end

	local operationsBudget = 50
	local operationsCounter = 0

	for x = 0, X - 1 do
		for z = 0, Z - 1 do
			local a, b, c, d = positionGrid[x][z], positionGrid[x+1][z], positionGrid[x][z+1], positionGrid[x+1][z+1]

			local w1, w2 = draw3dTriangle(a, b, c)
			local w3, w4 = draw3dTriangle(b, c, d)

			paintWedge(w1); paintWedge(w2); paintWedge(w3); paintWedge(w4)

			w1.Parent = chunkModel
			w2.Parent = chunkModel
			w3.Parent = chunkModel
			w4.Parent = chunkModel

			if not fastload then
				operationsCounter = operationsCounter + 4
				if operationsCounter >= operationsBudget then
					task.wait()
					operationsCounter = 0
				end
			end
		end
	end

	addWater(chunk)
	if chunk.waterPart then
		chunk.waterPart.Parent = chunkModel
	end

	chunkModel.Parent = workspace
	chunk.model = chunkModel

	return chunk
end

function Chunk:Destroy(fastload)
	if self.model then
		self.model.Parent = returnBuffer.Model
		for _, child in pairs(self.model:GetChildren()) do
			child.Parent = returnBuffer.Part
			if child.Name ~= "Water" then
				PartCache:ReturnPart(child)
			else
				child:Destroy()
			end 
		end
	end
end

function Chunk:IsReady()
	return self.isReady
end

return Chunk

Biomes.lua

local Biomes = {}

local BIOME_NOISE_SCALE = 15000 

local BIOME_SEED = 12345

-- ===================================================================
--                  COMPLETE BIOME DEFINITIONS
-- ===================================================================

Biomes.Definitions = {
	Plains = {
		Name = "Plains", 
		HeightScale = 100, 
		TerrainSmoothness = 20, 
		HasWater = true, 
		WaterLevel = -50,
		ApplyHeightAmplification = function(height) if height > 20 then height += (height - 20) * 1.2 end; return height end,
		HeightColors = {[-50] = Color3.fromRGB(216, 204, 157), [-10] = Color3.fromRGB(72, 113, 58), [0]   = Color3.fromRGB(82, 123, 68), [75]  = Color3.fromRGB(76, 80, 86), [150] = Color3.fromRGB(220, 220, 220)}
	},
	Desert = {
		Name = "Desert", 
		HeightScale = 40, 
		TerrainSmoothness = 15, 
		HasWater = false, 
		WaterLevel = -100,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-50] = Color3.fromRGB(240, 225, 179), [20]  = Color3.fromRGB(216, 204, 157), [50]  = Color3.fromRGB(181, 154, 119)}
	},
	Mountains = {
		Name = "Mountains", 
		HeightScale = 400, 
		TerrainSmoothness = 30, 
		HasWater = true, 
		WaterLevel = -50,
		ApplyHeightAmplification = function(height) if height > 10 then height += (height - 10) * 2.5 end; if height < -20 then height += (height + 20) * 1.5 end; return height end,
		HeightColors = {[-50] = Color3.fromRGB(72, 113, 58), [50]  = Color3.fromRGB(76, 80, 86), [200] = Color3.fromRGB(139, 143, 149), [350] = Color3.fromRGB(220, 220, 220)}
	},
	Canyonlands = {
		Name = "Canyonlands", 
		HeightScale = 200, 
		TerrainSmoothness = 18, 
		HasWater = true, 
		WaterLevel = -120,
		ApplyHeightAmplification = function(height) if height > 60 then return 150 + (height-60) * 0.1 end; return -110 end,
		HeightColors = {[-115] = Color3.fromRGB(181, 154, 119),[-100] = Color3.fromRGB(168, 95, 52), [150]  = Color3.fromRGB(205, 113, 63)}
	},
	Marshlands = {
		Name = "Marshlands", 
		HeightScale = 8, 
		TerrainSmoothness = 25, 
		HasWater = true, 
		WaterLevel = -6,
		ApplyHeightAmplification = function(height) return height * 0.5 end,
		HeightColors = {[-10] = Color3.fromRGB(86, 76, 56), [-7]  = Color3.fromRGB(60, 94, 72), [-4]  = Color3.fromRGB(106, 118, 73)}
	},
	VolcanicWastes = {
		Name = "Volcanic Wastes", 
		HeightScale = 150, 
		TerrainSmoothness = 12, 
		HasWater = false, 
		WaterLevel = -100,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-50] = Color3.fromRGB(40, 40, 45), [20]  = Color3.fromRGB(20, 20, 25), [80]  = Color3.fromRGB(80, 40, 40), [120] = Color3.fromRGB(255, 60, 0)}
	},
	Tundra = {
		Name = "Tundra", 
		HeightScale = 30, 
		TerrainSmoothness = 40, 
		HasWater = true, 
		WaterLevel = -40,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-30] = Color3.fromRGB(111, 111, 91), [0]   = Color3.fromRGB(140, 148, 108), [20]  = Color3.fromRGB(200, 200, 205)}
	},
	RedwoodForest = {
		Name = "Redwood Forest", 
		HeightScale = 250, 
		TerrainSmoothness = 28, 
		HasWater = true, 
		WaterLevel = -60,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-50] = Color3.fromRGB(94, 78, 59), [0]   = Color3.fromRGB(54, 73, 48), [150] = Color3.fromRGB(83, 85, 80)}
	},
	Savanna = {
		Name = "Savanna", 
		HeightScale = 25, 
		TerrainSmoothness = 50, 
		HasWater = true, 
		WaterLevel = -45,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-40] = Color3.fromRGB(210, 195, 147), [0]   = Color3.fromRGB(189, 172, 117), [20]  = Color3.fromRGB(160, 147, 104)}
	},
	IceSpikes = {
		Name = "Ice Spikes", 
		HeightScale = 300, 
		TerrainSmoothness = 8, 
		HasWater = false, WaterLevel = -100,
		ApplyHeightAmplification = function(height) return height * 1.5 end,
		HeightColors = {[-50] = Color3.fromRGB(118, 155, 185), [50]  = Color3.fromRGB(168, 205, 235), [250] = Color3.fromRGB(240, 245, 255)}
	},
	Highlands = {
		Name = "Highlands", 
		HeightScale = 200, 
		TerrainSmoothness = 45, 
		HasWater = true, WaterLevel = -70,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-60] = Color3.fromRGB(105, 111, 74), [0]   = Color3.fromRGB(82, 98, 61), [150] = Color3.fromRGB(111, 108, 99)}
	},
	Badlands = {
		Name = "Badlands", 
		HeightScale = 80, 
		TerrainSmoothness = 10, 
		HasWater = false, 
		WaterLevel = -100,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-50] = Color3.fromRGB(171, 131, 101), [0]   = Color3.fromRGB(205, 113, 63), [30]  = Color3.fromRGB(168, 95, 52), [60]  = Color3.fromRGB(111, 89, 73)}
	},
	Rainforest = {
		Name = "Rainforest", 
		HeightScale = 180, 
		TerrainSmoothness = 15, 
		HasWater = true, 
		WaterLevel = -30,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-20] = Color3.fromRGB(48, 68, 43), [50]  = Color3.fromRGB(57, 113, 65), [120] = Color3.fromRGB(80, 100, 75)}
	},
	Dunes = {
		Name = "Dunes", 
		HeightScale = 80, 
		TerrainSmoothness = 35, 
		HasWater = false, 
		WaterLevel = -100,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-60] = Color3.fromRGB(245, 230, 184), [50]  = Color3.fromRGB(215, 194, 147)}
	},
	Taiga = {
		Name = "Taiga", 
		HeightScale = 90, 
		TerrainSmoothness = 22, 
		HasWater = true, 
		WaterLevel = -50,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-40] = Color3.fromRGB(94, 84, 69), [0]   = Color3.fromRGB(64, 83, 68), [60]  = Color3.fromRGB(200, 205, 210)}
	},
	CrystallineChasm = {
		Name = "Crystalline Chasm", 
		HeightScale = 250, 
		TerrainSmoothness = 10, 
		HasWater = false, 
		WaterLevel = -200,
		ApplyHeightAmplification = function(height) return -math.abs(height) - 50 end,
		HeightColors = {[-300] = Color3.fromRGB(80, 40, 120), [-150] = Color3.fromRGB(150, 90, 200),[-60]  = Color3.fromRGB(210, 160, 230)}
	},
	Deadlands = {
		Name = "Deadlands", 
		HeightScale = 40, 
		TerrainSmoothness = 18, 
		HasWater = false, 
		WaterLevel = -80,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-30] = Color3.fromRGB(80, 78, 70), [10]  = Color3.fromRGB(120, 115, 100), [30]  = Color3.fromRGB(90, 88, 80)}
	},
	Oasis = {
		Name = "Oasis", 
		HeightScale = 60, 
		TerrainSmoothness = 30, 
		HasWater = true, 
		WaterLevel = -10,
		ApplyHeightAmplification = function(height) return height - (50 / (1 + (height/50)^2)) end,
		HeightColors = {[-40] = Color3.fromRGB(216, 204, 157), [-15] = Color3.fromRGB(72, 113, 58), [20]  = Color3.fromRGB(240, 225, 179)}
	},
	CherryBlossomGrove = {
		Name = "Cherry Blossom Grove", 
		HeightScale = 50, 
		TerrainSmoothness = 35, 
		HasWater = true, 
		WaterLevel = -20,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-15] = Color3.fromRGB(82, 123, 68), [10]  = Color3.fromRGB(255, 183, 197), [30]  = Color3.fromRGB(255, 135, 157)}
	},
	FungalCaves = {
		Name = "Fungal Caves", 
		HeightScale = 100, 
		TerrainSmoothness = 12, 
		HasWater = true, 
		WaterLevel = -90,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-80] = Color3.fromRGB(85, 71, 97), [-20] = Color3.fromRGB(143, 63, 133), [50]  = Color3.fromRGB(61, 53, 69)}
	},
	MangroveForest = {
		Name = "Mangrove Forest", 
		HeightScale = 5, 
		TerrainSmoothness = 20, 
		HasWater = true, 
		WaterLevel = -2,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-4] = Color3.fromRGB(71, 64, 50), [0]  = Color3.fromRGB(56, 82, 64)}
	},
	Prairie = {
		Name = "Prairie", 
		HeightScale = 15, 
		TerrainSmoothness = 60, 
		HasWater = false, 
		WaterLevel = -50,
		ApplyHeightAmplification = function(height) return height end,
		HeightColors = {[-10] = Color3.fromRGB(117, 131, 81), [10]  = Color3.fromRGB(147, 151, 101)}
	},
	SkyIslands = {
		Name = "Sky Islands", 
		HeightScale = 600, 
		TerrainSmoothness = 40, 
		HasWater = false, 
		WaterLevel = -500,
		ApplyHeightAmplification = function(height) if height < 150 then return -400 end; return 200 + (height - 150) end,
		HeightColors = {[-400] = Color3.fromRGB(20, 20, 20), [-350] = Color3.fromRGB(80, 70, 60), [200]  = Color3.fromRGB(72, 113, 58), [400]  = Color3.fromRGB(120, 120, 120)}
	},
}


-- ===================================================================
--                  BIOME SELECTION LOGIC
-- ===================================================================

Biomes.SortedList = {
	{Value = -1.00, Biome = Biomes.Definitions.SkyIslands},
	{Value = -0.90, Biome = Biomes.Definitions.CrystallineChasm},
	{Value = -0.80, Biome = Biomes.Definitions.IceSpikes},
	{Value = -0.70, Biome = Biomes.Definitions.VolcanicWastes},
	{Value = -0.60, Biome = Biomes.Definitions.Mountains},
	{Value = -0.50, Biome = Biomes.Definitions.Canyonlands},
	{Value = -0.40, Biome = Biomes.Definitions.Highlands},
	{Value = -0.30, Biome = Biomes.Definitions.Badlands},
	{Value = -0.20, Biome = Biomes.Definitions.Tundra},
	{Value = -0.10, Biome = Biomes.Definitions.Taiga},
	{Value = -0.05, Biome = Biomes.Definitions.RedwoodForest},
	{Value =  0.00, Biome = Biomes.Definitions.Plains},
	{Value =  0.10, Biome = Biomes.Definitions.Prairie},
	{Value =  0.20, Biome = Biomes.Definitions.CherryBlossomGrove},
	{Value =  0.30, Biome = Biomes.Definitions.Rainforest},
	{Value =  0.40, Biome = Biomes.Definitions.Savanna},
	{Value =  0.50, Biome = Biomes.Definitions.FungalCaves},
	{Value =  0.60, Biome = Biomes.Definitions.Marshlands},
	{Value =  0.70, Biome = Biomes.Definitions.MangroveForest},
	{Value =  0.80, Biome = Biomes.Definitions.Oasis},
	{Value =  0.90, Biome = Biomes.Definitions.Dunes},
	{Value =  0.95, Biome = Biomes.Definitions.Deadlands},
	{Value =  1.00, Biome = Biomes.Definitions.Desert},
}

function Biomes.getBiomeData(worldX, worldZ)
	if not worldX or not worldZ then return Biomes.SortedList[12].Biome, Biomes.SortedList[12].Biome, 0.5 end

	local biomeNoise = math.noise(
		BIOME_SEED,
		worldX / BIOME_NOISE_SCALE,
		worldZ / BIOME_NOISE_SCALE
	)

	local biomeA, biomeB
	for i = 1, #Biomes.SortedList - 1 do
		if biomeNoise >= Biomes.SortedList[i].Value and biomeNoise < Biomes.SortedList[i+1].Value then
			biomeA = Biomes.SortedList[i]
			biomeB = Biomes.SortedList[i+1]
			break
		end
	end

	if not biomeA then
		if biomeNoise < Biomes.SortedList[1].Value then
			return Biomes.SortedList[1].Biome, Biomes.SortedList[1].Biome, 0
		else
			return Biomes.SortedList[#Biomes.SortedList].Biome, Biomes.SortedList[#Biomes.SortedList].Biome, 1
		end
	end

	local alpha = (biomeNoise - biomeA.Value) / (biomeB.Value - biomeA.Value)

	return biomeA.Biome, biomeB.Biome, alpha
end

return Biomes

I know there are some witchcraft developers out here who know how to make anything work. So, would you recommend I go about creating a system where the world moves around the player? Especially one that can handle thousands of parts at once.

As a side, if you see any issues with my scripts or improvements that can be made, please let me know (I’m still learning). Thanks!

I see you are updating chanks every 0.5 seconds, moreover you are cloning these chunks and not using a technique like Partcache to cache these triangle chunks in memory.

Therefore I would suggest for you to look at partcache and replace the :Clone() function in your code.

1 Like

Thank you so much! Is this the partcache in question?

Using PartCache (I hope what I linked to was correct), I’ve updated the scripts with your suggested improvement. Thanks!