Need Help with dungeon-like procedural room generation

End Goal
my goal at the end is to be able to put in a random number (like 0,100) and have the game make a sort of grid of rooms connecting to eachother, but the way i have it right now makes a straight line, and the alternate would overlap the grid parts

Current situation

right now i have it set up so once a left is placed, only a right can be placed so theres no chance of going back and overlapping yet but i want to remove that so the map isnt just one straight line, as well as i could add 3 way corridors that have more than one direction, but i cant figure out at all how to get it so the grid placements do not overlap

** the code working as is**
(this video shows the code down below)

code working without perameters
this version of the code has the “left” and “right” values disabled, how id like it to be since it looks more random, but with this way it often overlaps other parts

things i have tried

Ive looked through multiple things on the devforum and google, but their either not what im looking for or straight up dont work
for trying to fix the overlapping ive tried :Gettouchingparts, .touched, and (partpos-part2pos).magnitude but those sort of solutions didnt work or i had added them wrong

i had tried using scripts made by other people but they yet again werent exactly what i was looking for or werent customizable enough for the game i am trying to make

i had made this code from scratch and it would be nice not to start over again if anyone has any solutions, i was thinking maybe a table or module script would work but im not sure exactly how id approach a solution like that

here is my code, sorry about how messy it is i wasnt expecting to have this much trouble with the procedural generation if you have any questions about the code feel free to ask

wait(5)



local huh = false
local gridd = game.ServerStorage.Grid.GridA

local left = false







local lastpart = nil

local function main(times, block)
	local tries = 10
	wait()
	local good = false
	local best = nil
	repeat
		if lastpart and tries > 0 then
			if not lastpart then
				local done = false
				repeat
					wait()
					local items = workspace.Grid:GetChildren()
					local randomItemy = items[math.random(1, #items)]
					if randomItemy:FindFirstChild("Directions") then
						if randomItemy:FindFirstChild("Directions"):FindFirstChild("Open") then
							lastpart = randomItemy
							done = true
						end
					end
				until done == true
			end
			
			wait()
			local itemas = lastpart:WaitForChild("Directions"):GetChildren()
			for i,part in pairs(lastpart:WaitForChild("Directions"):GetChildren()) do

				if part.Name == "Open" then



					local newgrid = block:Clone()
					newgrid.Parent = workspace.Grid
					local itemsn = newgrid.Directions:GetChildren()
					local randomItemn = itemsn[math.random(1, #itemsn)]




					local randomItem = newgrid.Directions:WaitForChild("Connect")
					newgrid.PrimaryPart = randomItem
					wait()


					local ooga = false
					local done = false


					newgrid:SetPrimaryPartCFrame(part.CFrame * CFrame.Angles(0,math.rad(180),0))

					local happened = false
					if ooga == false then
						tries = tries - 1
						good = true
						wait()
						part:Destroy()
						randomItem:Destroy()

						for i,v in pairs(lastpart.Directions:GetChildren()) do
							
						end

						lastpart = newgrid
					end
				elseif part.Name == "Closed" then
					local random = math.random(0,100)
					if random <= 50 then
						local itemsn = game.ServerStorage.GridFolder.ExtraRooms:GetChildren()
						local randomItemn = itemsn[math.random(1, #itemsn)]
						local newgrid = randomItemn:Clone()
						newgrid.Parent = workspace.Grid
						local randomItem = newgrid.Directions:WaitForChild("Connect")
						newgrid.PrimaryPart = randomItem
						newgrid:SetPrimaryPartCFrame(part.CFrame * CFrame.Angles(0,math.rad(180),0))
						part:Destroy()
						
						wait()
						newgrid.PrimaryPart:Destroy()
						
					else
						part.CanCollide = true
						part.Size = Vector3.new(45,30,9)
						
					end
				end
				
			end
		end
	until good == true
	
end

local function endo(times)
	local tries = 10
	wait()
	local good = false
	local best = nil
	repeat
		if lastpart and tries > 0 then
			if not lastpart then
				local done = false
				repeat
					wait()
					local items = workspace.Grid:GetChildren()
					local randomItemy = items[math.random(1, #items)]
					if randomItemy:FindFirstChild("Directions") then
						if randomItemy:FindFirstChild("Directions"):FindFirstChild("Open") then
							lastpart = randomItemy
							done = true
						end
					end
				until done == true
			end

			wait()
			local itemas = lastpart:WaitForChild("Directions"):GetChildren()
			local part = itemas[math.random(1, #itemas)]
			if part.Name == "Open" then

				local newgrid = game.ServerStorage.Grid.GridEnd:Clone()
				newgrid.Parent = workspace.Grid
				local items = newgrid.Directions:GetChildren()
				local randomItem = items[math.random(1, #items)]


				

				newgrid.PrimaryPart = randomItem
				wait()
				newgrid:SetPrimaryPartCFrame(part.CFrame * CFrame.Angles(0,math.rad(180),0))

				local ooga = false
				for _,m in pairs(newgrid.GridBlock:GetTouchingParts()) do
					if m.Name == "GridBlock" then
						newgrid:Destroy()
						ooga = true
						local done = false
						repeat
							wait()
							local items = workspace.Grid:GetChildren()
							local randomItemy = items[math.random(1, #items)]
							if randomItemy:FindFirstChild("Directions") then
								if randomItemy:FindFirstChild("Directions"):FindFirstChild("Open") then
									lastpart = randomItemy
									done = true
								end
							end
						until done == true
					end
				end



				local happened = false
				if ooga == false then
					tries = tries - 1
					good = true
					wait()
					part:Destroy()
					randomItem:Destroy()
					
					for i,v in pairs(lastpart.Directions:GetChildren()) do


					end

					lastpart = newgrid

				end
			end
		end
	until good == true

end
local startt = false
local straightline = math.random(0,7)

local function retry(times)
	
	if lastpart then
		repeat
			times = times - 1
				if times == 1 then
					endo(times)
				else
					
				if straightline > 0 then
					straightline = straightline - 1
					local itemsn = game.ServerStorage.GridFolder.Straights:GetChildren()
					local randomItemn = itemsn[math.random(1, #itemsn)]


					main(times, randomItemn)
				else
					if left == false then
						left = true
						local itemsn = game.ServerStorage.GridFolder.Lefts:GetChildren()
						local randomItemn = itemsn[math.random(1, #itemsn)]

						main(times, randomItemn)
						straightline = math.random(0,7)
					elseif left == true then
						left = false
						local itemsn = game.ServerStorage.GridFolder.Rights:GetChildren()
						local randomItemn = itemsn[math.random(1, #itemsn)]

						main(times, randomItemn)
						straightline = math.random(0,7)
					end
				end
				end
			

			until times <= 0
	else
		if startt == false then
			startt = true
			local newgrid = gridd:Clone()
			newgrid.Parent = workspace.Grid
			newgrid.PrimaryPart = newgrid.GridBlock
			newgrid:SetPrimaryPartCFrame(CFrame.new(0,0,0))
			lastpart = newgrid
		
		end
	end
end


repeat
	wait(5)
	retry(100)
until true == false

Instead of generating all rooms at once, why not generate the new room once a player opens the door and delete the previous previous room? So that the path will not be blocked by the previous dungeon paths.

i am intending for the game to be multiplayer, i suppose the best sort of way to describe it would be the same sort of procedural generation as warframe and the whole line would need to be open so players could be at different points throughout the procedurally generated map
i appreciate the reply though! : )

1 Like

You could make a grid of blocks with bool values, If a room is generated there then add true and if the grid piece its generating onto is already true then re-generate it.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.