How would I go about creating an infinite road generation system?

Hi!

I am trying to create an infinite road generation system, but so far no luck.

Basically what I need, is a system that creates an infinite road system, with highways, intersections, interchanges, dirt roads etc.

I use terrain as the ground, and I am using nodes to create each road. Each node can be at a different height it all depends on whether the terrain is flat or hilly or whatever.

Currently, each “chunk” represents a part of the road, and in each chunk there are 16x16=256 nodes, that are used to ensure that the road curves with the terrain, resulting in an bumpy or hilly road or whatever. (This is what I want btw)

However, so far, the nodes aren’t placed directly on top of the terrain, which makes the road act the same way, it stays flat, and doesn’t realistically touch the ground in some areas.

In the image you can also see that the road, doesn’t really have nice curves, rather it starts exactly on the left top node. Please also note that I made it so that the nodes on the side, have to be shared across the bordering chunk(s).

As you can see in the image below, only the top left node of the first chunk and the bottom right node of the next chunk are shared, instead of all the top nodes and all the bottom nodes.

What I basically want to achieve with the nodes, is assume that you are in blender, and you have a cube, with 16x16 vertices on the top, now if you grab some of them, the mesh deforms with it. This is basically what I want to mimic or directly replicate if possible (don’t think it is) in Roblox Studio.

EDIT: Forgot to put the script:

wait(7)

-- ROAD TYPES CONFIGURATION
local roadTypes = {
	["asphalt"] = {
		material = Enum.Material.Asphalt,
		color = Color3.fromRGB(50, 50, 50),
		thickness = 0.2, -- Thickness in studs
		width = 33, -- Road width in studs
		length = 64, -- Road length in studs
		maxHeightDifference = 10, -- Max height difference between nodes in a chunk
		isTerrainPainted = false, -- Asphalt uses parts, not terrain
	},
	-- Add more road types here as needed (e.g., dirt road, gravel, etc.)
}

-- SETTINGS
local seed = 12345 -- Change to your desired seed for reproducibility
math.randomseed(seed)

local settings = {
	maxHighways = 5, -- Maximum number of highways
	maxHighwayLength = 5000, -- Max highway length (in studs) before creating an interchange
	maxRoadAngle = 65, -- Max angle for road turns
	maxIntersectionAngle = 90, -- Max angle for intersections
	defaultRoadType = "asphalt", -- Default road type
	highwayRoadType = "asphalt", -- Default highway type
}

-- HELPER FUNCTIONS
local function getTerrainHeightAt(position)
	-- Get the terrain height at a given position
	local terrain = workspace.Terrain
	local region = Region3.new(position - Vector3.new(2, 2, 2), position + Vector3.new(2, 2, 2))
	local voxelResolution = 4 -- Voxel resolution level
	local voxelData, positions = terrain:ReadVoxels(region, voxelResolution)

	-- Ensure voxelData and positions are valid
	if voxelData and voxelData[1] and voxelData[1][1] and voxelData[1][1][1] then
		local material, pos = voxelData[1][1][1], positions[1][1][1]
		if pos then
			return pos.Y -- Return the Y-coordinate of the position
		end
	end

	-- Default to a height of 0 if no voxel data is available
	return 0
end

local function createNodeVisual(position)
	-- Create a visual node
	local sphere = Instance.new("Part")
	sphere.Shape = Enum.PartType.Ball
	sphere.Size = Vector3.new(0.5, 0.5, 0.5)
	sphere.Anchored = true
	sphere.Material = Enum.Material.Neon
	sphere.Color = Color3.fromRGB(255, 0, 0) -- Red Neon Color
	sphere.Position = position
	sphere.Parent = workspace
end

-- Generates a single road chunk
local function generateRoadChunk(origin, roadType)
	local config = roadTypes[roadType]
	local nodes = {}
	local width = config.width
	local length = config.length
	local thickness = config.thickness
	local nodeSpacingX = width / 15 -- Spacing between nodes in the X direction
	local nodeSpacingZ = length / 15 -- Spacing between nodes in the Z direction

	-- Generate 16x16 nodes across the surface
	for x = 0, 15 do
		nodes[x] = {}
		for z = 0, 15 do
			local position = origin + Vector3.new(x * nodeSpacingX, 0, z * nodeSpacingZ)
			local terrainHeight = getTerrainHeightAt(position)
			position = Vector3.new(position.X, terrainHeight, position.Z)

			-- Store node position
			nodes[x][z] = position

			-- Create a visual for the node
			createNodeVisual(position)
		end
	end

	-- Create road parts between nodes
	for x = 0, 14 do
		for z = 0, 14 do
			local node1 = nodes[x][z]
			local node2 = nodes[x + 1][z]
			local node3 = nodes[x][z + 1]
			local node4 = nodes[x + 1][z + 1]

			-- Create a part to fill the space between the nodes
			local roadPart = Instance.new("Part")
			roadPart.Size = Vector3.new(nodeSpacingX, thickness, nodeSpacingZ)
			roadPart.Anchored = true
			roadPart.Material = config.material
			roadPart.Color = config.color

			-- Calculate the average height for the part's center
			local avgHeight = (node1.Y + node2.Y + node3.Y + node4.Y) / 4
			roadPart.CFrame = CFrame.new(
				(node1 + node2 + node3 + node4) / 4,
				Vector3.new(0, avgHeight, 0)
			)
			roadPart.Parent = workspace
		end
	end

	-- Return edge nodes for connecting chunks
	local edgeNodes = {}
	for z = 0, 15 do
		table.insert(edgeNodes, nodes[15][z]) -- Right edge
	end
	for x = 0, 15 do
		table.insert(edgeNodes, nodes[x][15]) -- Bottom edge
	end
	return edgeNodes
end

-- Main road generation function
local function generateRoadSystem(startPosition)
	local currentPosition = startPosition
	local currentDirection = Vector3.new(1, 0, 0) -- Default direction
	local previousRoadType = settings.defaultRoadType
	local highwayCount = 0
	local previousEdgeNodes = {}

	for i = 1, settings.maxHighwayLength do
		-- Decide next road type
		local roadType = previousRoadType

		-- Generate road chunk
		local edgeNodes = generateRoadChunk(currentPosition, roadType)

		-- Shift position for next chunk
		currentPosition = edgeNodes[#edgeNodes]

		-- Update edge nodes for the next chunk
		previousEdgeNodes = edgeNodes

		-- Handle interchanges and intersections
		if math.random(1, 100) < 10 then -- 10% chance of an intersection
			highwayCount = highwayCount + 1
			if highwayCount >= settings.maxHighways then
				break
			end

			-- Create an intersection (can expand to generate additional roads)
			local intersectionPosition = currentPosition + Vector3.new(math.random(-50, 50), 0, math.random(-50, 50))
			currentPosition = intersectionPosition
		end

		-- Update the previous road type
		previousRoadType = roadType
	end
end

-- START GENERATION
generateRoadSystem(Vector3.new(0, 0, 0)) -- Start at the origin

Any tips/help is appreciated!

each road should have StartAttachment and EndAttachment, every new road should pivot its StartAttachment to the last’s EndAttachment, then just orientate them to align what ur car orientation is

The easiest, most efficient, and good looking method would be to just have a bunch of premade models, each containing nodes where other roads connect. Then generating a starting model and branching out from there

a simple way to improve this system that you already have is to raycast a point below each node, and then adjust the roads height based on that, this would create a road just above the surface, however, may be quite bumpy. a way to fix this would be to use some sort of smoothing algorithm, maybe averaging the road on a straight gradient making sure it doesn’t collide with the terrain at any point?

like baseparts said though, an even simpler way would just to make premade roads, then branching out from a starting point, which can create random roads if done correctly. the only problem would be that the terrain isn’t smooth, so you would need a way to connect them without intersecting the terrain

hope this helps

I had been wondering this myself for quite some time, I wanted to create a game like An Infinite Road Trip but didn’t know where to even start. I found this YouTube video to be quite helpful and it might just help you too. Although for learning about scripting I would recommend the DevForum rather than YouTube but it might give you some good pointers.

Hope this is helpful, and good luck with your project!

Right now the main issue is trying to make parts behave like meshes in blender where you drag vertices down or up and the mesh deforms with it.

I need to find a solution for that, the other already has an solution I just need to code it right.

just get the length of your road , every time you spawn a road increase the next position z or x axis “depends on which direction you are spawning the road”

I came up with a solution that will work, but it’s a bit tricky…

Instead of trying to deform parts, I will use small triangles in a 2x2 square. So in each square 2 triangles and 4 nodes on each edge of the square, 3 nodes for each triangle, where 2 nodes are shared between the 2:

image

This will result in a working solution to ensure that the roads curve with the terrain.

However an issue with this, I haven’t find a way to fix this is the fact that I can’t change the size/position of the triangle by the edges (nodes).

If I use pivots and only change the position I get something like this when I move the bottom right node in the image above:

image
image

So does anyone have a way to make this happen? I can’t seem to find any yet.