I am working currently on a game similar to crossy road and have the map generation side of it complete. It works fine and I’m wondering how I could add the map elements (trees, bushes, rocks, etc) spawn randomly while also not having RNG trap the player but make sure there is always a space for the player to move foreward.
One idea i had is with each floor piece could have an invisible part in the middle of each grid of the floor that would be able to spawn a random map obstacle depending on the floor type (eg: if ground type is grass then spawn either a tree, bush, or nothing). But the issue would still remain of making sure this doesn’t block the player from moving.
Script in ServerScriptService that generates a random map seed:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local startingRandom = 25267357
ReplicatedStorage.seed.Value = math.random() * startingRandom --numberValue being given a random number
Players.PlayerAdded:Connect(function(player)
player:WaitForChild("PlayerGui"):WaitForChild("respawn").Frame.TextButton.MouseButton1Click:Connect(function()
ReplicatedStorage.seed.Value = math.random() * startingRandom
end)
end)
LocalScript in ReplicatedFirst that generates each floor piece randomly:
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
if not game:IsLoaded() then game.Loaded:Wait() end
local player = Players.LocalPlayer
local part = Workspace.startingEnvironment:WaitForChild("start") --the connecting piece from the starting floor that isnt random, where the next generated piece will connect
local random = Random.new(ReplicatedStorage.seed.Value)
local mapReset = part:Clone()
mapReset.Parent = ReplicatedStorage.mapReset
local models = ReplicatedStorage.models:GetChildren()
table.sort(models, function(a, b) return a.Name < b.Name end)
local debounce = true
RunService.Heartbeat:Connect(function(deltaTime)
for _,v in pairs(workspace:GetDescendants()) do
if v:IsA("Texture") then
v:Destroy()
end
end
if not player.Character then return end
if (part.Position - player.Character:GetPivot().Position).Magnitude > 200 then return end
local model = models[random:NextInteger(1, #models)]:Clone()
model:PivotTo(part.CFrame)
model.start:Destroy()
model.End.Transparency = 1
model.Parent = workspace.environment.ground
part:Destroy()
part = model.End
ReplicatedStorage.seed.Changed:Connect(function()
if debounce then
debounce = false
print("respawning")
task.wait(1.2)
for _,x in pairs(Workspace.environment.ground:GetChildren())do
x:Destroy()
end
part = ReplicatedStorage.mapReset["start"]:Clone()
part.Parent = Workspace.startingEnvironment
local modelReload = models[random:NextInteger(1, #models)]:Clone()
modelReload:PivotTo(part.CFrame)
modelReload.start:Destroy()
modelReload.End.Transparency = 1
modelReload.Parent = workspace.environment.ground
part:Destroy()
part = modelReload.End
debounce = true
end
end)
end)