How would i go about fixing overlapping tiles in my procedural generation?

recently I’ve been making a new game that requires procedural generation as apart of it’s core gameplay, I’ve gotten the primary generation done but I’ve come into a problem, the tiles usually end up overlapping a lot and become just big conglomeration of tiles.
(screenshot provided to show what i mean.)

I’d really appreciate it if someone could correct my code and possibly fix this issue for me as it’s really hard for me to figure out a possible solution.
(though i might just be dumb and it’s just really easy to fix :sweat_smile:)

(code provided below)

if (script:GetAttribute('Generated') == true) then
	return
end
script:SetAttribute('Generated', true)

local RandomBiome: Folder = Biomes[Rand:NextInteger(1,#Biomes)]

local Tiles: Folder = RandomBiome:FindFirstChild('Tiles')
local TilesChildren: {} = Tiles:GetChildren()

local Center: Model = RandomBiome:FindFirstChild('Center'):Clone()
Center.Parent = Map

local CenterPoints: Folder = Center:FindFirstChild('Points')

for _,LaneNumber: number in {1,2,3,4} do
	print(`Generating Lane {LaneNumber}!`)

	local LaneFolder: Folder = TilesContainer:FindFirstChild(`Lane{LaneNumber}`)
	local CenterPoint: BasePart = CenterPoints:FindFirstChild(`Point{LaneNumber}`)

	local MinimumLength = 50
	local MaximumLength = 75
	local Length = Rand:NextInteger(MinimumLength,MaximumLength)

	for Index=1,Length do
		task.wait(0.1)

		local ChosenTile: Model = Tiles['Normal']

		if (Index >= math.random(4,7)) then
			local AvailableTiles = {}
			for _,Tile: Model in TilesChildren do
				table.insert(AvailableTiles,Tile.Name)
			end

			RemoveTilesInCooldown(AvailableTiles)

			ChosenTile = Tiles[AvailableTiles[Rand:NextInteger(1,#AvailableTiles)]]
		end

		if (Index == 1) then
			local Tile: Model = ChosenTile:Clone()

			local Type = Tile.Name
			Tile:SetAttribute('Type',Type)

			Tile.Name = `Tile_{Index}`
			Tile.Parent = LaneFolder
			Tile:PivotTo(CenterPoint.CFrame)
		elseif (Index > 1) then
			local PreviousTile: Model = LaneFolder:FindFirstChild(`Tile_{Index-1}`)
			local FrontPoint: BasePart = PreviousTile:FindFirstChild('Points'):FindFirstChild('FrontPoint')

			local Tile: Model = ChosenTile:Clone()

			local Type = Tile.Name
			Tile:SetAttribute('Type',Type)

			UpdateCooldowns()
			CreateCooldown(Type)

			Tile.Name = `Tile_{Index}`
			Tile.Parent = LaneFolder
			Tile:PivotTo(FrontPoint.CFrame)
		end
	end

end

Cooldowns:ClearAllChildren()
print('Done Generating!')
1 Like

Your tiles are probably overlapping because you’re only using the previous tile’s FrontPoint CFrame. Try aligning the new tile’s BackPoint to the previous tile’s FrontPoint instead:

local PreviousFront = PreviousTile.Points.FrontPoint
local NewBack = Tile.Points.BackPoint

Tile:PivotTo(PreviousFront.CFrame * NewBack.CFrame:Inverse())

Also make sure each lane has its own unique start point, because shared points can cause tiles to spawn on top of each other.

i’ve done that already actually, and the pivoting and such of the tiles themselves isn’t the issue, it’s just when it generates there’s often overlap

here’s what the generation basically looks like with a better screenshot


it generates normally sometimes, but that’s usually just dependent on rng and that sucks.

within this same generation i showed previously there often can be clusters like this which aren’t ideal


i’d assume there’s a way to make a system which checks if it’s overlapping and uses a different tile to avoid overlap, but i have ZERO clue how to do that :sob:

I think the issue is that the generator is placing tiles randomly without checking if the space is already taken so sometimes they overlap depending on RNG You could add a check before placing a tile generate the tile check if it’s colliding with existing tiles and if it is remove it and try another tile/rotation This way the generation won’t rely only on luck

local overlapParams = OverlapParams.new()
overlapParams.FilterType = Enum.RaycastFilterType.Include
overlapParams.FilterDescendantsInstances = {workspace.GeneratedTiles}

local function CanPlaceTile(tile)
	local touching = workspace:GetPartBoundsInBox(
		tile:GetPivot(),
		tile.PrimaryPart.Size,
		overlapParams
	)

	return #touching == 0
end

-- When generating a tile:
tile:PivotTo(position)

if CanPlaceTile(tile) then
	tile.Parent = workspace.GeneratedTiles
else
	tile:Destroy()
	-- Try another tile or rotation
end

You might also want to add a retry loop so if a tile overlaps it automatically tries a different one instead of just failing This is a pretty common way to handle procedural generation If it doesn’t work, sorry, I just tried my best to help. I’m not 100% sure how your generation system is set up

this actually ended up working extremely well!
i had to do some little tweaks, but i got it mostly working, and tiles don’t overlap hardly at all (they still do but it’s in only extremely rare chances, and i should be able to fix that myself.)
greatly appreciate your help :smiley:

1 Like

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