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?
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?
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?
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
I know this issue has been solved, but doenst that have to do something with ChangeHistoryService? Or am i wrong