I want to make my game no longer cause long freezes, and prevent the game from generating dead ends in between segments that happen to connect to each other indirectly.
The issue is as stated in the goal. I need to get the lag spikes caused by dead end generation to cease (when normal segments can’t generate and has to generate a dead end, randomly causes script execution exhaustion for no visible reason) and I have no idea where it could be coming from, other than the random segment generator function maybe infinitely looping. Dead ends were working as intended up until I changed how segment generation chance works to be based on individual spawn chances instead of category based spawn chances. Dead ends should only be spawning if the specified exit of a segment reached a length beyond a number of times, or if another segment is in the way, and Dead ends should never generate more than once in the same place.
For more information: BoundingBoxes in each segment serves to prevent other segments from clipping into it, and EndGuard is a special case version of BoundingBoxes which covers individual exits, and only takes effect when a dead end tries to generate inside of it.
I have tried so many solutions that I have been burnt out trying them all. No sort of roblox documentation is going to help, I’m afraid.
All of these issues I have ran into almost always originated from the MapGeneration script, since it is the main script that handles all of the generation, and the segments themselves are certainly not the problem.
Code:
local SS = game:GetService("ServerStorage")
local sfolder = workspace:FindFirstChild("Segments")
sfolder.Parent = SS
local live = sfolder.Live:GetChildren()
local dead = sfolder.Dead:GetChildren()
local pool_live = {}
local pool_dead = {}
function addMarbles(p, t)
for _,v in ipairs(t) do
local q = v:GetAttribute("SpawnChance")
if q and math.floor(q) >= 1 then
for n=1, math.floor(q) do
table.insert(p, v)
end
end
end
end
function rollMarbles(p, removeResult, default)
if #p == 0 then
warn("Pool is empty. Resorting to default, if any.")
return default
end
local r = p[math.random(1,math.max(1, #p))]
if removeResult then
while table.find(p, r) do
table.remove(p, table.find(p, r))
end
end
return r
end
addMarbles(pool_live, live)
addMarbles(pool_dead, dead)
print("live #:", #pool_live)
print("dead #:", #pool_dead)
local maze = workspace:FindFirstChild("Maze")
local beginning = maze:FindFirstChild("Spawn"):FindFirstChild("Beginning")
local ev = game:GetService("ReplicatedStorage"):FindFirstChild("Recam")
-- Settings
local config = {
max_segment_count = script:GetAttribute("MaxSegmentCount"),
max_segment_combo = script:GetAttribute("MaxSegmentCombo"),
max_branches = script:GetAttribute("MaxBranches"),
delay_multiplier = script:GetAttribute("DelayMultiplier"),
manual_gen = script:GetAttribute("DebugMode")
}
local segmentCount = 0
local threads = 0
local function subfunc(func, ...) -- Run a function independently from the current thread.
local rem = Instance.new('BindableEvent')
rem.Event:Connect(func)
rem:Fire(...)
end
function connecteval(root, obj, ignoreCollisions)
local rot = CFrame.Angles(0, math.rad(root.Orientation.Y), 0)
obj:SetPrimaryPartCFrame(obj:GetPrimaryPartCFrame() * rot)
local oy = math.rad(root.Orientation.Y)
local increment = Vector3.new(-math.sin(oy), 0, -math.cos(oy))
print(increment)
local final = root.Position + increment
local distance = final - obj.PrimaryPart.Position
if ignoreCollisions == nil then
ignoreCollisions = obj:GetAttribute("IgnoresCollisions")
end
print("Ignoring Collisions:", ignoreCollisions)
local oldParent = obj.Parent
obj.Parent = maze.Segments
if config.manual_gen then
ev:FireAllClients(obj.BoundingBoxes.Main)
end
for _,p in ipairs(obj:GetDescendants()) do
if not p:IsA("BasePart") then
continue
end
p.Position += distance
if config.manual_gen then
p.CanCollide = false
end
if p:FindFirstAncestor("BoundingBoxes") or p:FindFirstAncestor("EndGuard")
or p:FindFirstAncestor("Connections") then
p.Transparency = config.manual_gen and .8 or 1
end
local collisionG = ignoreCollisions and "EndGuard" or "BoundingBoxes"
print("Collision Focus:", collisionG)
local attempts = {}
p.Touched:Connect(function()end)
for _,b in ipairs(p:GetTouchingParts()) do
local db = b.Parent.Parent
local droot = root:FindFirstAncestorOfClass("Model")
table.insert(attempts, "\nBoundery: " .. b.Parent.Name .. "\nColliding: " .. p.Parent.Name .. "\nSegment: " .. db.Name)
if b.Parent.Name == collisionG and p.Parent.Name == "BoundingBoxes"
and db ~= obj and db.Parent.Parent == maze and
(not b:IsDescendantOf(droot)) then
obj.Parent = oldParent
return false,final
end
end
print(#attempts, "attempts:", attempts)
end
return true,final
end
function generate(start, segment, ignoreCollisions)
local obj = segment:Clone()
local success,final = connecteval(start, obj, ignoreCollisions)
local curcon,sub
if success or ignoreCollisions then
local cs = obj:FindFirstChild("Connections")
if cs then
curcon = cs:FindFirstChild("End")
sub = cs:FindFirstChild("Sub")
end
else
obj = sfolder.Dead.Dead_Wall:Clone()
_,final = connecteval(start, obj, true)
end
if not obj:GetAttribute("IsDeadEnd") then
segmentCount += 1
print("s:", segmentCount)
end
return curcon, sub, success, final
end
function randGenerate(start, n)
local lpool_live,lpool_dead = table.clone(pool_live),table.clone(pool_dead)
local sid,temp, curcon,ocs,success,final
while not success do
print({lpool_live, lpool_dead})
local variant
if segmentCount < config.max_segment_count then
if n <= config.max_segment_combo then
variant = lpool_live
else
variant = lpool_dead
end
else
variant = lpool_dead
end
temp = rollMarbles(variant, false)
print("v:", #variant)
while variant == lpool_live and n == config.max_segment_combo and not temp:GetAttribute("ContainsSubEnds") do
temp = rollMarbles(variant, true, sfolder.Dead.Dead_Wall)
print("v:", #variant)
wait(.071)
if temp == sfolder.Dead.Dead_Wall then
break
end
end
curcon,ocs,success,final = generate(start, temp)
if temp == sfolder.Dead.Dead_Wall and not success then
warn("Cannot spawn fallback. Imma just skip this one.")
break
end
end
return curcon, ocs, n, success, final
end
function fullGenerate(start)
threads += 1
local curcon = start
for n=1,config.max_segment_combo+1 do
if config.manual_gen then
ev.OnServerEvent:Wait()
else
wait(.1 * config.delay_multiplier)
end
local subcon
local newcon, ocs, n, success, final = randGenerate(curcon, n)
if not (newcon or ocs) then
print("dead end")
break
end
if ocs then
subcon = ocs:GetChildren()
for _,sv in ipairs(subcon) do
subfunc(fullGenerate, sv)
wait(.05)
end
end
if newcon then
curcon = newcon
end
subcon = subcon or {}
print(newcon, ocs, n, success, final)
end
threads -= 1
if threads <= 0 then
ev:FireAllClients()
end
end
if not config.manual_gen then
wait(8)
for n=3,1,-1 do
print(n)
wait(1)
end
end
fullGenerate(beginning)
If you need to copy the game, just request it and I’ll PM you the game link.