Hello, it’s my first time using actors, and I’m creating a multithreaded client-sided terrain generation system.
How would I make it such that the work I’m trying to compute doesn’t interfere with the render step? Currently it produces lag spikes as shown:
I have important parts of my scripts attached below, I’m not including the entire scripts for simplicity’s sake, however I can create a pastebin if needed.
Actor Local Script:
actor:BindToMessageParallel("GenerateChunk", function (id: Vector3int16)
-- TODO: skip if you can guarantee it will be completely empty/not visible
local bounds = TGService:GetChunkBounds(id)
local region = TGService:BoundsToRegion(bounds)
local materials = CreateVoxelArray(region, Enum.Material.Air) :: {{{ Enum.Material }}}
local occupancy = CreateVoxelArray(region, 0) :: {{{ number }}}
local chunkVoxelWidth = #occupancy
-- xv is the voxel index of the chunk in the x direction
for xv = 1, chunkVoxelWidth do
for zv = 1, chunkVoxelWidth do
local x = bounds.Min.X + xv * 4 -- chunk voxel to world position
local z = bounds.Min.Z + zv * 4
local terrainHeight = math.noise(
x * HorizontalScale,
z * HorizontalScale,
0
)
* VerticalScale
if terrainHeight < bounds.Min.Y then continue end
for yv = 1, chunkVoxelWidth do
local y = bounds.Min.Y + yv * 4
if y < terrainHeight then
occupancy[xv][yv][zv] = math.min((terrainHeight - y) / 4, 1)
materials[xv][yv][zv] = Enum.Material.Sand
end
end
end
end
task.synchronize()
Terrain:WriteVoxels(region, 4, materials, occupancy)
end)
Terrain Generation Service Script:
function TGService.Start(self: TerrainGenerationService)
for i = 1, 64 do
local worker = script.TerrainWorker:Clone()
worker.Parent = ReplicatedFirst
table.insert(self.Workers, worker)
worker.Name ..= #self.Workers
end
RS.Heartbeat:Connect(function()
local renderedChunks = {}
local visibleChunks = self:GetVisibleChunks()
if not visibleChunks then return end
local chunksToGenerate = {}
for _, chunk in visibleChunks do
if table.find(self.RenderedChunks, chunk) then
table.insert(renderedChunks, chunk)
table.remove(self.RenderedChunks, table.find(self.RenderedChunks, chunk))
continue
end
table.insert(chunksToGenerate, chunk)
table.insert(renderedChunks, chunk)
end
self:GenerateChunks(chunksToGenerate)
self:ClearChunks(self.RenderedChunks)
self.RenderedChunks = renderedChunks
end)
end
function TGService.GenerateChunks(self: TerrainGenerationService, ids: { Vector3int16 })
for _, id in ids do
local worker = self:GetNextWorker()
worker:SendMessage("GenerateChunk", id)
end
end
function TGService.GetNextWorker(self: TerrainGenerationService)
local worker = self.Workers[self.NextWorkerIdx]
self.NextWorkerIdx = (self.NextWorkerIdx) % #self.Workers + 1
return worker
end