So, as an example of ExtremeBuilder15’s method, I would personally use code like the block at the bottom.
By the way, all functions used in this code use Vector2
for formatting positions for the sake of concept. Because of this, I provided a simple function that converts Vector3
to Vector2
near the top.
math.randomseed(tick())
local ModelGen = {
Enabled = false,
Grid_Size = 25,
Seed = Vector2.new(math.random(),math.random()),
Render_Radius = 5,
Templates = script.Templates:GetChildren(),
Space = {}
}
ModelGen.Time_Before_Next_Update = ModelGen.Grid_Size/16
function ModelGen:toVector2(v3)
return Vector2.new(math.floor(v3.X + 0.5),math.floor(v3.Z + 0.5))
end
-- set and get functions for Space, since it will error if the first index is nil.
function ModelGen.Space:set(index1,index2,value)
if self[index1] then
self[index1][index2] = value
else
self[index1] = {}
self[index1][index2] = value
end
end
function ModelGen.Space:get(index1,index2)
if self[index1] then
return self[index1][index2]
else
return nil
end
end
function ModelGen:SetEnabled(bool) -- simplest way to disable ModelGen:AutoUpdate
self.Enabled = bool
end
-- Base function for creating a model at a certain position in Space
function ModelGen:Create(AtPos)
if not self.Space:get(AtPos.X, AtPos.Y) then
local mid = math.clamp(math.noise(AtPos.X*math.pi + self.Seed.X,AtPos.Y*math.pi + self.Seed.Y),-0.499,0.5) + 0.5
local rc = math.ceil(#self.Templates*mid)
local clone = self.Templates[rc]:Clone()
clone:SetPrimaryPartCFrame(CFrame.new(AtPos.X*self.Grid_Size,0,AtPos.Y*self.Grid_Size))
clone.Parent = workspace
self.Space:set(AtPos.X, AtPos.Y, clone)
end
end
-- Base function for destroying a model at a certain position in Space
function ModelGen:Destroy(AtPos)
local model = self.Space:get(AtPos.X, AtPos.Y)
if model then
model:Destroy()
self.Space:set(AtPos.X, AtPos.Y, nil)
end
end
function ModelGen:Update(AtPos)
-- Destroy any chunks that are outside of the Render_Radius and still loaded
for x, tab in pairs(self.Space) do
if x ~= "set" and x ~= "get" then
for y, value in pairs(tab) do
if math.abs(AtPos.X - x) > self.Render_Radius or math.abs(AtPos.Y - y) > self.Render_Radius then
self:Destroy(Vector2.new(x, y))
end
end
end
end
-- Create any chunks that are in the Render_Radius and not loaded
for x = -self.Render_Radius, self.Render_Radius do
for y = -self.Render_Radius, self.Render_Radius do
self:Create(Vector2.new(AtPos.X + x,AtPos.Y + y))
end
end
end
function ModelGen:AutoUpdate(rootPart) -- takes the HumanoidRootPart as its argument
self.Enabled = true
self:Update(self:toVector2(rootPart.Position/self.Grid_Size))
while wait(self.Time_Before_Next_Update) and self.Enabled do
self:Update(self:toVector2(rootPart.Position/self.Grid_Size))
end
end
return ModelGen
This is written in a ModuleScript
format for ease of use, I guess. It is best used if required from a Server Script however.
I should also note that this script saves metadata in the form of ModelGen.MetaSpace
, which means that if you come back to this chunk after it has been destroyed, it will generate a new chunk that is identical to the old one, which is pretty nice in my opinion.
Edit 1: I haven’t tested this at all by the way, so it might have some errors.
Edit 2: It has now been tested and it works for me.
Edit 3: I used the suggestion from Khodin to use math.noise, no more MetaSpace, still works perfectly.