How would i go about making a map generation algorithm

To clarify, I want to make a automatically generated map with premade tiles and link them up. I am not sure how to make wave function collapse, if there is any other alternatives that’d be nice (the tiles are also of varying sizes)

You can have certain sets of tiles generate in certain patterns based ones that you save, so it would be like a matrix of configuration

Like this chunk of tiles has this biome, in this pattern, rotated this way.
Then you have more tiles but another pattern you select!
Basically just chunk configuration

So to have certain patterns hard coded and to use them to determine possible tiles for exits and entrances?

1 Like

And if you need an exit or entrance, generate the chunk to be compatible with the new tiles!

Theres some sorta plugin for that
https://create.roblox.com/marketplace/asset/5459691708?viewFromStudio=true&keyword=&searchId=6581BFDE-777C-4955-9A2D-073289A611DF
If you have some sorta plugin (RoPro etc.) You could look through the source code.

Update: I dont think the plugins work anymore since they updated the website, plugins arent updated yet

You can still use the old links by removing create from the front and replacing marketplace with catalog.

What kind of generation are you going for? Dungeon generation with rooms and hallways? More naturally generated dungeons like Pokemon Mystery Dungeon? Or are you talking about terrain here?

Terrain.

sdsdrfsd

This here is an table with thousands of lines of code.
This is the source code

local http = require(script.http)

local toolbar = plugin:CreateToolbar("reshanie")
local button = toolbar:CreateButton("Render Buildings", "Select an area on a map and render its roads and buildings in your game.", "rbxassetid://5459666484")

local API_URL = "http://c.paric.xyz"  -- production

-- API_URL = "http://192.168.1.91:5000"  -- dev

local ACTIVE_SESSION
local SESSION_TIMEOUT
local S_DATA

local Parts = require(script.parts)

local UI = script.BTUI
local link_box = UI.Frame.Link

UI.Enabled = false
UI.Parent = game:GetService("CoreGui")
local active = false

local function setActive(a)
    active = a
    UI.Enabled = a
    button:SetActive(a)
end

button.Click:Connect(function()
    setActive(not active)

    if active then
        if (not ACTIVE_SESSION) or tick() >= SESSION_TIMEOUT then
            local resp = http.post(API_URL .. "/session", {log=false}):json()

            ACTIVE_SESSION = resp.id
            link_box.Text = resp.url
            SESSION_TIMEOUT = tick() + 9*60  -- 9 minutes
        end
    end
end)

coroutine.wrap(function()
    while wait(1) do
        if SESSION_TIMEOUT and tick() >= SESSION_TIMEOUT then
            ACTIVE_SESSION = nil
            setActive(false)
		end
		
		local succ, err = pcall(function()
	        if ACTIVE_SESSION then
				local resp = http.get(API_URL .. "/session/" .. ACTIVE_SESSION, {log=false})
				
				if resp.ok then  -- request failed due to timeout?
					resp = resp:json()
					if not resp.pending then  -- user picked region
						link_box.Text = "Loading..."
						setActive(false)

						S_DATA = resp
						--coroutine.wrap(function()
						render(resp.nw, resp.se, resp.origin, resp.selection, resp.heightmap)
						--end)()
						link_box.Text = resp.url

						http.post(API_URL .. "/session/" .. ACTIVE_SESSION, {data={pending=true}, log=false})
						SESSION_TIMEOUT = tick() + 4*60  -- 4 minutes
					end
				else
					print(resp.code, resp.text)
					ACTIVE_SESSION = nil
				end
			end
		end)
		
		if not succ then
			print("HttpError " .. err)
		end
    end
end)()

-- RENDERING FUNCS --

local Buildings = require(script.buildings)
local Roads = require(script.roads)
local Util = require(script.util)

local function intersects(nw, se, coords, swap)
    for _, c_ in ipairs(coords) do
        local c = c_
        if swap then
            c = {c_[2], c_[1]}
        end

        if c[1] < nw[1] and c[1] > se[1] then
            if c[2] > nw[2] and c[2] < se[2] then
                return true
            end
        end
    end


    return false
end

-- MAIN RENDER CODE --

local SCALE = 364567


function render(nw, se, origin, selection, heightmap)
	local bldg_folder = workspace:FindFirstChild("RenderedBuildings")
	if bldg_folder == nil then
    	bldg_folder = Instance.new("Folder", workspace)
		bldg_folder.Name = "RenderedBuildings"
	end
		
	local origin_val = bldg_folder:FindFirstChild("Origin")
	if origin_val then
		origin = {
			origin_val.Value.X,
			origin_val.Value.Y
		}
	else
		origin_val = Instance.new("Vector3Value", bldg_folder)
		origin_val.Name = "Origin"
		origin_val.Value = Vector3.new(origin[1], origin[2], 0)
	end
	
	
	-- terrain
	
	if heightmap ~= nil then
		local tl_x, tl_z = Util.coord_to_pos(selection[1][1], selection[1][2], origin, SCALE)
		local br_x, br_z = Util.coord_to_pos(selection[2][1], selection[2][2], origin, SCALE)
		
		-- width/height of selection
		local width = math.abs(br_x-tl_x)
		local length = math.abs(br_z-tl_z)
		
		-- get heightmap pixel resolution (stud/pixel)
		local res_x = width/heightmap.width
		local res_z = length/heightmap.height
		
		local offset
		
		local offset_val = bldg_folder:FindFirstChild("MinHeight")
		if offset_val then
			offset = offset_val.Value
		else
			offset_val = Instance.new("NumberValue", bldg_folder)
			offset_val.Name = "MinHeight"
			offset_val.Value = heightmap.min
			offset = heightmap.min
		end
		
		for x=0, heightmap.width-2 do
			for z=0, heightmap.height-2 do
				local a = math.max(0, heightmap.data[("%s,%s"):format(x, z)]) - offset
				local b = math.max(0, heightmap.data[("%s,%s"):format(x+1, z)]) - offset
				local c = math.max(0, heightmap.data[("%s,%s"):format(x, z+1)]) - offset
				local d = math.max(0, heightmap.data[("%s,%s"):format(x+1, z+1)]) - offset
				
				a = Vector3.new(x*res_x + tl_x, a*3.28084, z*res_z + tl_z)
				b = Vector3.new((x+1)*res_x + tl_x, b*3.28084, z*res_z + tl_z)
				c = Vector3.new(x*res_x + tl_x, c*3.28084, (z+1)*res_z + tl_z)
				d = Vector3.new((x+1)*res_x + tl_x, d*3.28084, (z+1)*res_z + tl_z)
				
				local tri0 = Parts.Triangle(a, b, c, {}, 4)
				local tri1 = Parts.Triangle(c, b, d, {}, 4)
				
				for _, tri in ipairs({tri0, tri1}) do
					for _, wedge in ipairs(tri:GetChildren()) do
						local mat = Enum.Material.Grass
						if heightmap.data[("%s,%s"):format(x, z)] <= 1 then
							mat = Enum.Material.Sand
						end
						if heightmap.data[("%s,%s"):format(x, z)] <= 0 then
							mat = Enum.Material.Water
						end
						
						pcall(function()
							workspace.Terrain:FillWedge(wedge.CFrame, wedge.Size, mat)
						end)
						
					end
					
					tri:Destroy()
				end
			end
			
			wait()
		end
	end	
	
	workspace.Terrain:SetMaterialColor(Enum.Material.Asphalt, Color3.fromRGB(77, 79, 84))

    for x=nw[1], se[1] do
        for y=nw[2], se[2] do
            -- roads

            local url = ("https://tile.nextzen.org/tilezen/vector/v1/256/all/%s/%s/%s.json"):format(16, x, y)
            local rdata = http.get(url, {query={api_key="N-y9kIrESbaIApeIkLrXCA"}}):json()
            print("road data fetched")

			for _, feature in ipairs(rdata.roads.features) do
				pcall(function()
	                if feature.geometry.type == "LineString" then
	                    if intersects(selection[1], selection[2], feature.geometry.coordinates, true) then
	                        local way = {["type"]=feature.properties.kind_detail}

	                        way.pts = feature.geometry.coordinates

	                        local prnt = bldg_folder:FindFirstChild(feature.properties.name or "!") or Instance.new("Folder", bldg_folder)
	                        prnt.Name = feature.properties.name or "Road"
							
							if heightmap ~= nil then
								Roads.DrawRoad(way, origin, SCALE, prnt, heightmap.maxh, true)
							else
								Roads.DrawRoad(way, origin, SCALE, prnt, nil, false)
							end
	                    end
	                elseif feature.geometry.type == "MultiLineString" then
	                    for _, line in ipairs(feature.geometry.coordinates) do
	                        if intersects(selection[1], selection[2], line, true) then
	                            local way = {["type"]=feature.properties.kind_detail}

	                            way.pts = line

	                            local prnt = bldg_folder:FindFirstChild(feature.properties.name or "!") or Instance.new("Folder", bldg_folder)
	                            prnt.Name = feature.properties.name or "Road"

								if heightmap ~= nil then
									Roads.DrawRoad(way, origin, SCALE, prnt, heightmap.maxh, true)
								else
									Roads.DrawRoad(way, origin, SCALE, prnt, nil, false)
								end
	                        end
	                    end
					end
				end)
            end

            -- buildings

            for _, bldg in ipairs(rdata.buildings.features) do
                if bldg.geometry.type ~= "Point" then
                    local coords = bldg.geometry.coordinates
                    if type(coords[1][1]) ~= "number" then
                        coords = coords[1]
                    end
					bldg.geometry.coordinates = coords

					if #coords > 0 then
						pcall(function()
	                        if intersects(selection[1], selection[2], coords, true) then
	                            local b = Buildings.fromJSON(bldg, origin, SCALE)
								-- b.tall = true
								
								if b.name == "Building" or (bldg_folder:FindFirstChild(b.name) == nil) then
		                            local prnt = Instance.new("Model", bldg_folder)
		                            prnt.Name = b.name
		                            
		                            pcall(function()
		                            b:Render(nil, prnt)
									end)
								end
							end
						end)
                    end
                end
            end

            wait()
        end
	end

end

INFO: THIS DOES NOT WORK, YOU NEED TO INSTALL THE WHOLE DATA AND CUSTOMIZE IT ON YOUR OWN.

sorry you went out of your way to get this, but what i need is a tile based map, not a terrain based

i tried doing this but i cant get the exits and entrances to line up, i have bothj on seperate models, under the folder links to show how it can link, i cant get two models to line up a links, any ideas?

If you can make sure every tile is the exact same size and every tile matches up in some way being rotated

Could you give a more specific explanation or preferably a visual example of what you’re trying to do exactly?

its okay i guess

i figured it out with help from a friend, thanks everyone

Im actually very interested? Do u mind asking how?

Basically you have a list of entrances and exits for 2 rooms to connect and you basically just make it so every entrance and exits front vector looks out, and you can set the primary part of the model to connect to be the entrance which will be connected to the exit and set primary part cframe that and make it so it looks forward using the .LookVector of the exit and verify that it is a possible layout using some hitbox collisions, if its possible place it. and at the end i do a trimming bit where if there is no exits leading anywhere it’ll destroy the tile basically and make sure everything is linking to eachother and that there isnt floating bits of the map just incase and i do it via brute force trying every available exit

1 Like

I know this issue has been solved, but doenst that have to do something with ChangeHistoryService? Or am i wrong