The straight strip becomes a closed rounded-rect loop (~7,400 studs/lap) built from centerline samples, Shuto-C1 style. Landmark zones: - Daikoku Futo PA: drivable meet lot off the south straight (guardrail gap entrance), painted bays, flood-light towers, bilingual sign — cars now SPAWN at Daikoku like a proper meet - Rainbow Bridge over the east straight: white suspension towers with beacons, cross beams, rainbow-colored rail lights - Odaiba beyond the bridge: blocky Unicorn Gundam (glowing eyes, horn, pink psycho-frame strips) + 16-cabin neon ferris wheel - Yamate-style tunnel enclosing the whole north straight - Tokyo Tower replica (orange/white tiers, two lit decks, antenna) west - Tatsumi PA overlook pad NE - City-core skyline inside the loop + outer ring skipping landmark zones - Bilingual green highway signs via SurfaceGui (JP + EN) Gameplay contract preserved: SpawnPad/FinishLine/KillWall names intact, kill walls hug every road edge except the Daikoku entrance.
This commit is contained in:
+10
-3
@@ -37,9 +37,16 @@ until this is true.
|
||||
|
||||
## M2 — More cars + polish (still no racing)
|
||||
- [ ] Import a few more real car meshes.
|
||||
- [x] Better map v2: 3200-stud expressway with gantry arches, a neon-lit
|
||||
tunnel, jump ramps (right lane; left stays flat), end wall, and a
|
||||
procedural neon skyline flanking the route.
|
||||
- [x] Better map v3 — **"Shuto Night Loop"**: closed ~7,400-stud circuit
|
||||
inspired by Tokyo's Shuto expressway, with real-Tokyo landmark zones:
|
||||
**Daikoku Futo PA** (drivable meet lot + painted bays + light towers —
|
||||
cars spawn here), **Rainbow Bridge** (suspension towers, rainbow rail
|
||||
lights), **Odaiba** beyond it (blocky Unicorn Gundam statue with glowing
|
||||
psycho-frame + giant neon ferris wheel), **Yamate-style tunnel** (north
|
||||
straight), **Tokyo Tower** replica (west), **Tatsumi PA** overlook,
|
||||
bilingual green highway signs (大黒PA, レインボーブリッジ, お台場, 辰巳PA),
|
||||
city-core + outer-ring procedural skyline. Not geographically accurate —
|
||||
a themed loop, not a map of Tokyo.
|
||||
- [x] Day/night, weather mood: `src/server/Ambience.luau` — Atmosphere haze
|
||||
catching the neon, bloom + cool color-correction post effects, and a slow
|
||||
22:00→03:00 clock drift (10-min ping-pong) so the night sky breathes.
|
||||
|
||||
+574
-219
@@ -1,36 +1,56 @@
|
||||
--!strict
|
||||
--[[
|
||||
MapBuilder.luau (SERVER)
|
||||
Builds the "Tokyo expressway" map entirely from code — ZERO manual Studio
|
||||
building. v2 (M2 groundwork): the straight highway now runs through a
|
||||
sequence of FEATURES so driving it is actually interesting:
|
||||
v3 — "Shuto Night Loop": a Tokyo-expressway-inspired CIRCUIT, still built
|
||||
100% from code. The straight strip is gone; the road is now a closed
|
||||
rounded-rectangle loop (~7,400 studs/lap) modelled on the Shuto C1 vibe,
|
||||
with real-Tokyo landmark zones:
|
||||
|
||||
seg 0-1 open start (spawn pad)
|
||||
seg 2 overhead gantry arches
|
||||
seg 3 TUNNEL (enclosed, neon-striped ceiling)
|
||||
seg 4 jump ramps (side-by-side launch + landing, optional — road
|
||||
continues flat around them)
|
||||
seg 5-6 open run to the finish line
|
||||
seg 7 finish + runoff
|
||||
SOUTH straight — Daikoku Futo PA: the legendary meet spot. A big
|
||||
drivable parking area off the expressway with painted
|
||||
bays, light towers, and the spawn pad. Cars spawn HERE.
|
||||
EAST straight — Rainbow Bridge: white suspension towers, cross beams,
|
||||
rainbow-lit rails. Odaiba sits beyond it: a blocky
|
||||
Unicorn-Gundam-style statue + a giant neon ferris
|
||||
wheel (Palette Town wheel).
|
||||
NORTH straight — Yamate-style tunnel: long enclosed bore with strip
|
||||
lights (the C1/Yamate underground feel).
|
||||
WEST straight — City skyline run past a Tokyo Tower replica
|
||||
(orange/white, twin decks, antenna).
|
||||
Everywhere — procedural neon skyline inside and outside the loop,
|
||||
Tatsumi-PA-style overlook pad (visual, NE).
|
||||
|
||||
Neon skyline blocks flank the whole route outside the guardrails for the
|
||||
night-city mood. Everything is still named parts (SpawnPad, FinishLine,
|
||||
KillWall_*) so gameplay code keeps working when this is replaced by a
|
||||
modeled map later.
|
||||
NOT geographically accurate — it's a themed driving loop, not a map of
|
||||
Tokyo. Gameplay parts keep their contract names (SpawnPad, FinishLine,
|
||||
KillWall_*) so nothing else changes.
|
||||
]]
|
||||
|
||||
local Workspace = game:GetService("Workspace")
|
||||
|
||||
local MapBuilder = {}
|
||||
|
||||
-- Loop geometry ----------------------------------------------------------------
|
||||
local ROAD_WIDTH = 60
|
||||
local SEGMENT_LENGTH = 400
|
||||
local NUM_SEGMENTS = 8 -- total length = 3200 studs of highway
|
||||
local HX = 1200 -- half-extent X
|
||||
local HZ = 800 -- half-extent Z
|
||||
local CORNER_R = 300
|
||||
local STEP = 60 -- straight sample spacing
|
||||
local ARC_STEPS = 12 -- samples per 90-degree corner
|
||||
local WALL_HEIGHT = 26
|
||||
|
||||
local NEON_PINK = Color3.fromRGB(255, 40, 120)
|
||||
local NEON_BLUE = Color3.fromRGB(40, 160, 255)
|
||||
local NEON_TEAL = Color3.fromRGB(40, 230, 200)
|
||||
local RAINBOW = {
|
||||
Color3.fromRGB(255, 60, 60),
|
||||
Color3.fromRGB(255, 160, 40),
|
||||
Color3.fromRGB(250, 230, 60),
|
||||
Color3.fromRGB(80, 230, 90),
|
||||
Color3.fromRGB(60, 150, 255),
|
||||
Color3.fromRGB(170, 90, 255),
|
||||
}
|
||||
|
||||
local UP = Vector3.new(0, 1, 0)
|
||||
|
||||
local function part(props: { [string]: any }): Part
|
||||
local p = Instance.new("Part")
|
||||
@@ -49,232 +69,582 @@ local function det(seed: number): number
|
||||
return x - math.floor(x)
|
||||
end
|
||||
|
||||
-- Road deck ------------------------------------------------------------------
|
||||
local function buildRoad(parent: Folder)
|
||||
for i = 0, NUM_SEGMENTS - 1 do
|
||||
local z = -(i * SEGMENT_LENGTH) - SEGMENT_LENGTH / 2
|
||||
-- Green highway sign with bilingual text ---------------------------------------
|
||||
local function makeSign(text: string, cf: CFrame, parent: Instance)
|
||||
local board = part({
|
||||
Name = "Sign",
|
||||
Size = Vector3.new(30, 9, 1),
|
||||
CFrame = cf,
|
||||
Color = Color3.fromRGB(18, 90, 60),
|
||||
Material = Enum.Material.SmoothPlastic,
|
||||
})
|
||||
board.Parent = parent
|
||||
local gui = Instance.new("SurfaceGui")
|
||||
gui.Face = Enum.NormalId.Front
|
||||
gui.CanvasSize = Vector2.new(600, 180)
|
||||
gui.Parent = board
|
||||
local label = Instance.new("TextLabel")
|
||||
label.Size = UDim2.fromScale(1, 1)
|
||||
label.BackgroundTransparency = 1
|
||||
label.Font = Enum.Font.GothamBold
|
||||
label.TextColor3 = Color3.new(1, 1, 1)
|
||||
label.TextScaled = true
|
||||
label.Text = text
|
||||
label.Parent = gui
|
||||
end
|
||||
|
||||
-- Centerline samples ------------------------------------------------------------
|
||||
type Sample = { pos: Vector3, piece: string }
|
||||
|
||||
local function genSamples(): { Sample }
|
||||
local samples: { Sample } = {}
|
||||
local sx = HX - CORNER_R
|
||||
local sz = HZ - CORNER_R
|
||||
|
||||
local function push(pos: Vector3, piece: string)
|
||||
table.insert(samples, { pos = pos, piece = piece })
|
||||
end
|
||||
local function arc(cx: number, cz: number, a0: number, piece: string)
|
||||
for i = 1, ARC_STEPS do
|
||||
local a = a0 + (i / ARC_STEPS) * (math.pi / 2)
|
||||
push(Vector3.new(cx + CORNER_R * math.sin(a), 0, cz + CORNER_R * math.cos(a)), piece)
|
||||
end
|
||||
end
|
||||
|
||||
-- South straight (Daikoku side), heading +X
|
||||
local x = -sx
|
||||
while x <= sx do
|
||||
push(Vector3.new(x, 0, HZ), "south")
|
||||
x += STEP
|
||||
end
|
||||
arc(sx, sz, 0, "corner_se")
|
||||
-- East straight (Rainbow Bridge), heading -Z
|
||||
local z = sz - STEP
|
||||
while z >= -sz do
|
||||
push(Vector3.new(HX, 0, z), "east")
|
||||
z -= STEP
|
||||
end
|
||||
arc(sx, -sz, math.pi / 2, "corner_ne")
|
||||
-- North straight (Yamate tunnel), heading -X
|
||||
x = sx - STEP
|
||||
while x >= -sx do
|
||||
push(Vector3.new(x, 0, -HZ), "north")
|
||||
x -= STEP
|
||||
end
|
||||
arc(-sx, -sz, math.pi, "corner_nw")
|
||||
-- West straight (Tokyo Tower run), heading +Z
|
||||
z = -sz + STEP
|
||||
while z <= sz do
|
||||
push(Vector3.new(-HX, 0, z), "west")
|
||||
z += STEP
|
||||
end
|
||||
arc(-sx, sz, 3 * math.pi / 2, "corner_sw")
|
||||
|
||||
return samples
|
||||
end
|
||||
|
||||
-- Daikoku PA entrance gap on the south straight's OUTER side -------------------
|
||||
local DAIKOKU_GAP_HALF = 270
|
||||
|
||||
local function isDaikokuGap(mid: Vector3, piece: string, outer: boolean): boolean
|
||||
return piece == "south" and outer and math.abs(mid.X) < DAIKOKU_GAP_HALF
|
||||
end
|
||||
|
||||
-- The loop road itself ----------------------------------------------------------
|
||||
local function buildLoop(map: Folder, samples: { Sample })
|
||||
local roadFolder = Instance.new("Folder")
|
||||
roadFolder.Name = "Expressway"
|
||||
roadFolder.Parent = map
|
||||
|
||||
local n = #samples
|
||||
for i = 1, n do
|
||||
local a = samples[i]
|
||||
local b = samples[(i % n) + 1]
|
||||
local delta = b.pos - a.pos
|
||||
local len = delta.Magnitude + 2
|
||||
local mid = (a.pos + b.pos) / 2
|
||||
local dir = delta.Unit
|
||||
local right = dir:Cross(UP) -- always points OUTWARD on this loop
|
||||
local cf = CFrame.lookAt(mid, mid + dir)
|
||||
|
||||
local road = part({
|
||||
Name = "Road_" .. i,
|
||||
Size = Vector3.new(ROAD_WIDTH, 2, SEGMENT_LENGTH),
|
||||
Position = Vector3.new(0, 0, z),
|
||||
Size = Vector3.new(ROAD_WIDTH, 2, len),
|
||||
CFrame = cf,
|
||||
Color = Color3.fromRGB(28, 28, 34),
|
||||
Material = Enum.Material.Asphalt,
|
||||
})
|
||||
road.Parent = parent
|
||||
road.Parent = roadFolder
|
||||
|
||||
local line = part({
|
||||
Name = "Lane_" .. i,
|
||||
Size = Vector3.new(1, 2.1, SEGMENT_LENGTH),
|
||||
Position = Vector3.new(0, 0.1, z),
|
||||
Size = Vector3.new(1, 2.1, len),
|
||||
CFrame = cf * CFrame.new(0, 0.1, 0),
|
||||
Color = Color3.fromRGB(230, 200, 60),
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
line.Parent = parent
|
||||
line.Parent = roadFolder
|
||||
|
||||
local inTunnel = a.piece == "north"
|
||||
for _, side in ipairs({ -1, 1 }) do
|
||||
local outer = side == 1
|
||||
if isDaikokuGap(mid, a.piece, outer) then
|
||||
continue
|
||||
end
|
||||
local off = right * (side * (ROAD_WIDTH / 2))
|
||||
local rail = part({
|
||||
Name = "Guardrail",
|
||||
Size = Vector3.new(1.5, 4, len),
|
||||
CFrame = CFrame.lookAt(mid + off, mid + off + dir) * CFrame.new(0, 2, 0),
|
||||
Color = Color3.fromRGB(180, 180, 190),
|
||||
Material = Enum.Material.Metal,
|
||||
})
|
||||
rail.Parent = map
|
||||
|
||||
local woff = right * (side * (ROAD_WIDTH / 2 + 2))
|
||||
local wall = part({
|
||||
Name = "KillWall",
|
||||
Size = Vector3.new(2, WALL_HEIGHT, len + 6),
|
||||
CFrame = CFrame.lookAt(mid + woff, mid + woff + dir) * CFrame.new(0, WALL_HEIGHT / 2, 0),
|
||||
Transparency = 1,
|
||||
CanCollide = true,
|
||||
})
|
||||
wall.Parent = map
|
||||
|
||||
-- Rainbow rail lights on the bridge; neon pylons elsewhere (not in tunnel)
|
||||
if a.piece == "east" then
|
||||
local lamp = part({
|
||||
Name = "BridgeLight",
|
||||
Size = Vector3.new(1.2, 1.2, 1.2),
|
||||
CFrame = CFrame.new(mid + off + Vector3.new(0, 4.6, 0)),
|
||||
Color = RAINBOW[(i % #RAINBOW) + 1],
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
lamp.Parent = map
|
||||
elseif not inTunnel and i % 3 == 0 then
|
||||
local pylon = part({
|
||||
Name = "Pylon",
|
||||
Size = Vector3.new(1, 14, 1),
|
||||
CFrame = CFrame.new(mid + right * (side * (ROAD_WIDTH / 2 + 3)) + Vector3.new(0, 7, 0)),
|
||||
Color = outer and NEON_PINK or NEON_BLUE,
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
pylon.Parent = map
|
||||
end
|
||||
end
|
||||
|
||||
-- Yamate tunnel bore over the north straight -----------------------------
|
||||
if inTunnel then
|
||||
local h = 22
|
||||
for _, side in ipairs({ -1, 1 }) do
|
||||
local woff = right * (side * (ROAD_WIDTH / 2 + 1.5))
|
||||
local wallT = part({
|
||||
Name = "TunnelWall",
|
||||
Size = Vector3.new(3, h, len + 2),
|
||||
CFrame = CFrame.lookAt(mid + woff, mid + woff + dir) * CFrame.new(0, h / 2, 0),
|
||||
Color = Color3.fromRGB(38, 38, 46),
|
||||
Material = Enum.Material.Concrete,
|
||||
})
|
||||
wallT.Parent = map
|
||||
end
|
||||
local ceiling = part({
|
||||
Name = "TunnelCeiling",
|
||||
Size = Vector3.new(ROAD_WIDTH + 6, 2, len + 2),
|
||||
CFrame = cf * CFrame.new(0, h + 1, 0),
|
||||
Color = Color3.fromRGB(30, 30, 38),
|
||||
Material = Enum.Material.Concrete,
|
||||
})
|
||||
ceiling.Parent = map
|
||||
if i % 2 == 0 then
|
||||
local strip = part({
|
||||
Name = "TunnelLight",
|
||||
Size = Vector3.new(ROAD_WIDTH * 0.5, 0.6, 2),
|
||||
CFrame = cf * CFrame.new(0, h - 0.5, 0),
|
||||
Color = i % 4 == 0 and NEON_BLUE or NEON_TEAL,
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
strip.Parent = map
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Guardrails, kill walls, pylons ----------------------------------------------
|
||||
local function buildBarriers(map: Folder, totalLength: number)
|
||||
for _, side in ipairs({ -1, 1 }) do
|
||||
local x = side * (ROAD_WIDTH / 2)
|
||||
local rail = part({
|
||||
Name = "Guardrail_" .. (side == -1 and "L" or "R"),
|
||||
Size = Vector3.new(1.5, 4, totalLength),
|
||||
Position = Vector3.new(x, 2, -totalLength / 2),
|
||||
Color = Color3.fromRGB(180, 180, 190),
|
||||
-- Daikoku Futo PA ----------------------------------------------------------------
|
||||
local function buildDaikoku(map: Folder)
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "DaikokuPA"
|
||||
folder.Parent = map
|
||||
|
||||
local z0 = HZ + ROAD_WIDTH / 2 -- pad starts at the road's outer edge
|
||||
local depth = 220
|
||||
local halfW = 260
|
||||
|
||||
local pad = part({
|
||||
Name = "DaikokuPad",
|
||||
Size = Vector3.new(halfW * 2, 2, depth),
|
||||
CFrame = CFrame.new(0, 0, z0 + depth / 2),
|
||||
Color = Color3.fromRGB(34, 34, 40),
|
||||
Material = Enum.Material.Asphalt,
|
||||
})
|
||||
pad.Parent = folder
|
||||
|
||||
-- Painted parking bays (two rows, like the real lot's herringbone rows)
|
||||
for row = 0, 1 do
|
||||
local zRow = z0 + 70 + row * 90
|
||||
for bx = -5, 5 do
|
||||
local lineP = part({
|
||||
Name = "BayLine",
|
||||
Size = Vector3.new(1, 2.1, 22),
|
||||
CFrame = CFrame.new(bx * 42, 0.1, zRow),
|
||||
Color = Color3.fromRGB(230, 230, 235),
|
||||
Material = Enum.Material.SmoothPlastic,
|
||||
})
|
||||
lineP.Parent = folder
|
||||
end
|
||||
end
|
||||
|
||||
-- Light towers (the tall lot floods) + perimeter kill walls
|
||||
for _, corner in ipairs({
|
||||
{ -halfW + 20, z0 + 30 },
|
||||
{ halfW - 20, z0 + 30 },
|
||||
{ -halfW + 20, z0 + depth - 20 },
|
||||
{ halfW - 20, z0 + depth - 20 },
|
||||
}) do
|
||||
local px, pz = corner[1], corner[2]
|
||||
local pole = part({
|
||||
Name = "LightPole",
|
||||
Size = Vector3.new(2, 34, 2),
|
||||
CFrame = CFrame.new(px, 17, pz),
|
||||
Color = Color3.fromRGB(120, 120, 130),
|
||||
Material = Enum.Material.Metal,
|
||||
})
|
||||
rail.Parent = map
|
||||
pole.Parent = folder
|
||||
local head = part({
|
||||
Name = "LightHead",
|
||||
Size = Vector3.new(8, 1.5, 8),
|
||||
CFrame = CFrame.new(px, 34.5, pz),
|
||||
Color = Color3.fromRGB(255, 240, 200),
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
head.Parent = folder
|
||||
end
|
||||
|
||||
local wall = part({
|
||||
Name = "KillWall_" .. (side == -1 and "L" or "R"),
|
||||
Size = Vector3.new(2, WALL_HEIGHT, totalLength),
|
||||
Position = Vector3.new(x + side * 2, WALL_HEIGHT / 2, -totalLength / 2),
|
||||
-- Perimeter walls (invisible) on the three non-road sides
|
||||
local wallDefs = {
|
||||
{ Vector3.new(halfW * 2 + 4, WALL_HEIGHT, 2), CFrame.new(0, WALL_HEIGHT / 2, z0 + depth + 1) },
|
||||
{ Vector3.new(2, WALL_HEIGHT, depth + 4), CFrame.new(-halfW - 1, WALL_HEIGHT / 2, z0 + depth / 2) },
|
||||
{ Vector3.new(2, WALL_HEIGHT, depth + 4), CFrame.new(halfW + 1, WALL_HEIGHT / 2, z0 + depth / 2) },
|
||||
}
|
||||
for _, def in ipairs(wallDefs) do
|
||||
local w = part({
|
||||
Name = "KillWall_PA",
|
||||
Size = def[1],
|
||||
CFrame = def[2],
|
||||
Transparency = 1,
|
||||
CanCollide = true,
|
||||
})
|
||||
wall.Parent = map
|
||||
|
||||
for i = 0, math.floor(totalLength / 200) do
|
||||
local pylon = part({
|
||||
Name = "Pylon",
|
||||
Size = Vector3.new(1, 14, 1),
|
||||
Position = Vector3.new(x + side * 3, 7, -(i * 200)),
|
||||
Color = side == -1 and NEON_PINK or NEON_BLUE,
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
pylon.Parent = map
|
||||
end
|
||||
w.Parent = folder
|
||||
end
|
||||
|
||||
-- Spawn pad (cars appear here — Daikoku is the meet) -----------------------
|
||||
local spawn = part({
|
||||
Name = "SpawnPad",
|
||||
Size = Vector3.new(120, 1, 60),
|
||||
CFrame = CFrame.new(0, 1.2, z0 + 130),
|
||||
Color = Color3.fromRGB(20, 220, 120),
|
||||
Material = Enum.Material.Neon,
|
||||
Transparency = 0.4,
|
||||
})
|
||||
spawn.Parent = folder
|
||||
|
||||
makeSign("大黒PA DAIKOKU PA", CFrame.new(0, 12, z0 + 4) * CFrame.Angles(0, math.pi, 0), folder)
|
||||
end
|
||||
|
||||
-- Overhead gantry arches (segment 2) -------------------------------------------
|
||||
local function buildGantries(map: Folder)
|
||||
-- Rainbow Bridge towers over the east straight -----------------------------------
|
||||
local function buildRainbowBridge(map: Folder)
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Gantries"
|
||||
folder.Name = "RainbowBridge"
|
||||
folder.Parent = map
|
||||
|
||||
local segStart = 2 * SEGMENT_LENGTH
|
||||
for g = 0, 3 do
|
||||
local z = -(segStart + 60 + g * 90)
|
||||
for _, tz in ipairs({ 250, -250 }) do
|
||||
for _, side in ipairs({ -1, 1 }) do
|
||||
local post = part({
|
||||
Name = "GantryPost",
|
||||
Size = Vector3.new(2, 24, 2),
|
||||
Position = Vector3.new(side * (ROAD_WIDTH / 2 - 2), 12, z),
|
||||
Color = Color3.fromRGB(60, 60, 70),
|
||||
Material = Enum.Material.DiamondPlate,
|
||||
local tx = HX + side * (ROAD_WIDTH / 2 + 5)
|
||||
local tower = part({
|
||||
Name = "BridgeTower",
|
||||
Size = Vector3.new(6, 95, 6),
|
||||
CFrame = CFrame.new(tx, 47.5, tz),
|
||||
Color = Color3.fromRGB(235, 235, 240),
|
||||
Material = Enum.Material.Concrete,
|
||||
})
|
||||
post.Parent = folder
|
||||
tower.Parent = folder
|
||||
local beacon = part({
|
||||
Name = "TowerBeacon",
|
||||
Size = Vector3.new(2, 2, 2),
|
||||
CFrame = CFrame.new(tx, 96, tz),
|
||||
Color = Color3.fromRGB(255, 80, 80),
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
beacon.Parent = folder
|
||||
end
|
||||
local beam = part({
|
||||
Name = "GantryBeam",
|
||||
Size = Vector3.new(ROAD_WIDTH, 2.5, 2.5),
|
||||
Position = Vector3.new(0, 24, z),
|
||||
Color = g % 2 == 0 and NEON_TEAL or NEON_PINK,
|
||||
Material = Enum.Material.Neon,
|
||||
Name = "TowerBeam",
|
||||
Size = Vector3.new(ROAD_WIDTH + 22, 4, 5),
|
||||
CFrame = CFrame.new(HX, 62, tz),
|
||||
Color = Color3.fromRGB(235, 235, 240),
|
||||
Material = Enum.Material.Concrete,
|
||||
})
|
||||
beam.Parent = folder
|
||||
end
|
||||
makeSign(
|
||||
"レインボーブリッジ RAINBOW BRIDGE",
|
||||
CFrame.new(HX, 14, 330) * CFrame.Angles(0, math.pi / 2, 0),
|
||||
folder
|
||||
)
|
||||
end
|
||||
|
||||
-- Tunnel (segment 3): walls + ceiling with neon strips ---------------------------
|
||||
local function buildTunnel(map: Folder)
|
||||
-- Odaiba: blocky Unicorn-Gundam-style statue + giant ferris wheel ----------------
|
||||
local function buildOdaiba(map: Folder)
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Tunnel"
|
||||
folder.Name = "Odaiba"
|
||||
folder.Parent = map
|
||||
|
||||
local zCenter = -(3 * SEGMENT_LENGTH) - SEGMENT_LENGTH / 2
|
||||
local tunnelHeight = 22
|
||||
local gx, gz = HX + 150, 480 -- statue stands beyond the bridge's south end
|
||||
local white = Color3.fromRGB(235, 235, 240)
|
||||
|
||||
for _, side in ipairs({ -1, 1 }) do
|
||||
local wall = part({
|
||||
Name = "TunnelWall_" .. (side == -1 and "L" or "R"),
|
||||
Size = Vector3.new(3, tunnelHeight, SEGMENT_LENGTH),
|
||||
Position = Vector3.new(side * (ROAD_WIDTH / 2 + 1.5), tunnelHeight / 2, zCenter),
|
||||
Color = Color3.fromRGB(38, 38, 46),
|
||||
Material = Enum.Material.Concrete,
|
||||
local function block(name: string, size: Vector3, cf: CFrame, color: Color3, mat: Enum.Material?)
|
||||
local b = part({
|
||||
Name = name,
|
||||
Size = size,
|
||||
CFrame = cf,
|
||||
Color = color,
|
||||
Material = mat or Enum.Material.SmoothPlastic,
|
||||
})
|
||||
wall.Parent = folder
|
||||
b.Parent = folder
|
||||
return b
|
||||
end
|
||||
|
||||
local ceiling = part({
|
||||
Name = "TunnelCeiling",
|
||||
Size = Vector3.new(ROAD_WIDTH + 6, 2, SEGMENT_LENGTH),
|
||||
Position = Vector3.new(0, tunnelHeight + 1, zCenter),
|
||||
Color = Color3.fromRGB(30, 30, 38),
|
||||
Material = Enum.Material.Concrete,
|
||||
})
|
||||
ceiling.Parent = folder
|
||||
-- Base plinth
|
||||
block(
|
||||
"GundamBase",
|
||||
Vector3.new(40, 4, 40),
|
||||
CFrame.new(gx, 2, gz),
|
||||
Color3.fromRGB(60, 60, 70),
|
||||
Enum.Material.Concrete
|
||||
)
|
||||
-- Legs
|
||||
block("GundamLegL", Vector3.new(7, 20, 9), CFrame.new(gx - 6, 14, gz), white)
|
||||
block("GundamLegR", Vector3.new(7, 20, 9), CFrame.new(gx + 6, 14, gz), white)
|
||||
-- Hips + torso
|
||||
block("GundamHips", Vector3.new(16, 6, 10), CFrame.new(gx, 27, gz), white)
|
||||
block("GundamTorso", Vector3.new(15, 14, 10), CFrame.new(gx, 37, gz), white)
|
||||
block("GundamChest", Vector3.new(9, 5, 3), CFrame.new(gx, 39, gz - 6), Color3.fromRGB(60, 90, 220))
|
||||
block("GundamCore", Vector3.new(4, 3, 2), CFrame.new(gx, 34, gz - 6), Color3.fromRGB(230, 60, 60))
|
||||
-- Shoulders + arms
|
||||
block("GundamShoulderL", Vector3.new(6, 5, 7), CFrame.new(gx - 11, 42, gz), white)
|
||||
block("GundamShoulderR", Vector3.new(6, 5, 7), CFrame.new(gx + 11, 42, gz), white)
|
||||
block("GundamArmL", Vector3.new(4.5, 16, 5), CFrame.new(gx - 11, 32, gz), white)
|
||||
block("GundamArmR", Vector3.new(4.5, 16, 5), CFrame.new(gx + 11, 32, gz), white)
|
||||
-- Head with glowing eyes + unicorn horn
|
||||
block("GundamHead", Vector3.new(6, 5.5, 5.5), CFrame.new(gx, 47, gz), white)
|
||||
block("GundamEyes", Vector3.new(4, 1, 0.6), CFrame.new(gx, 47.5, gz - 2.8), NEON_TEAL, Enum.Material.Neon)
|
||||
block(
|
||||
"GundamHorn",
|
||||
Vector3.new(0.8, 6, 0.8),
|
||||
CFrame.new(gx, 53, gz - 1) * CFrame.Angles(math.rad(15), 0, 0),
|
||||
Color3.fromRGB(250, 220, 90),
|
||||
Enum.Material.Neon
|
||||
)
|
||||
-- Psycho-frame glow strips on the legs/torso (the Unicorn's pink frame)
|
||||
block("GundamFrameL", Vector3.new(1, 14, 1), CFrame.new(gx - 6, 14, gz - 4.6), NEON_PINK, Enum.Material.Neon)
|
||||
block("GundamFrameR", Vector3.new(1, 14, 1), CFrame.new(gx + 6, 14, gz - 4.6), NEON_PINK, Enum.Material.Neon)
|
||||
|
||||
-- Neon ceiling strips light the tunnel (one every 50 studs, alternating).
|
||||
for i = 0, math.floor(SEGMENT_LENGTH / 50) do
|
||||
local z = -(3 * SEGMENT_LENGTH) - i * 50
|
||||
local strip = part({
|
||||
Name = "TunnelLight",
|
||||
Size = Vector3.new(ROAD_WIDTH * 0.5, 0.6, 2),
|
||||
Position = Vector3.new(0, tunnelHeight - 0.5, z),
|
||||
Color = i % 2 == 0 and NEON_BLUE or NEON_TEAL,
|
||||
-- Giant ferris wheel (Palette Town) -----------------------------------------
|
||||
local wx, wz = HX + 240, 120
|
||||
local radius = 55
|
||||
local hub = CFrame.new(wx, radius + 24, wz)
|
||||
block(
|
||||
"WheelSupportL",
|
||||
Vector3.new(4, radius + 26, 4),
|
||||
CFrame.new(wx - 18, (radius + 24) / 2, wz) * CFrame.Angles(0, 0, math.rad(-14)),
|
||||
Color3.fromRGB(120, 120, 130),
|
||||
Enum.Material.Metal
|
||||
)
|
||||
block(
|
||||
"WheelSupportR",
|
||||
Vector3.new(4, radius + 26, 4),
|
||||
CFrame.new(wx + 18, (radius + 24) / 2, wz) * CFrame.Angles(0, 0, math.rad(14)),
|
||||
Color3.fromRGB(120, 120, 130),
|
||||
Enum.Material.Metal
|
||||
)
|
||||
block("WheelHub", Vector3.new(6, 6, 6), hub, Color3.fromRGB(200, 200, 210), Enum.Material.Metal)
|
||||
for k = 1, 16 do
|
||||
local ang = (k / 16) * math.pi * 2
|
||||
local cab = part({
|
||||
Name = "WheelCabin",
|
||||
Size = Vector3.new(4, 4, 4),
|
||||
Shape = Enum.PartType.Ball,
|
||||
CFrame = hub * CFrame.new(math.cos(ang) * radius, math.sin(ang) * radius, 0),
|
||||
Color = RAINBOW[(k % #RAINBOW) + 1],
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
strip.Parent = folder
|
||||
cab.Parent = folder
|
||||
if k % 2 == 0 then
|
||||
local spoke = part({
|
||||
Name = "WheelSpoke",
|
||||
Size = Vector3.new(0.8, radius, 0.8),
|
||||
CFrame = hub * CFrame.Angles(0, 0, ang) * CFrame.new(0, radius / 2, 0),
|
||||
Color = Color3.fromRGB(150, 150, 160),
|
||||
Material = Enum.Material.Metal,
|
||||
})
|
||||
spoke.Parent = folder
|
||||
end
|
||||
end
|
||||
|
||||
makeSign("お台場 ODAIBA", CFrame.new(HX + 60, 12, 300) * CFrame.Angles(0, math.pi / 2, 0), folder)
|
||||
end
|
||||
|
||||
-- Jump ramps (segment 4): launch + landing pair on the right half ---------------
|
||||
local function buildRamps(map: Folder)
|
||||
-- Tokyo Tower replica on the west side --------------------------------------------
|
||||
local function buildTokyoTower(map: Folder)
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Ramps"
|
||||
folder.Name = "TokyoTower"
|
||||
folder.Parent = map
|
||||
|
||||
local zStart = 4 * SEGMENT_LENGTH + 100
|
||||
local rampX = ROAD_WIDTH / 4 -- right half of the road; left half stays flat
|
||||
|
||||
-- Launch ramp: a wedge sloping up in the direction of travel (-Z).
|
||||
local launch = Instance.new("WedgePart")
|
||||
launch.Anchored = true
|
||||
launch.Name = "RampLaunch"
|
||||
launch.Size = Vector3.new(ROAD_WIDTH / 2 - 4, 10, 48)
|
||||
-- WedgePart slopes from its -Z (tall) to +Z (thin edge); car travels -Z,
|
||||
-- so face the thin edge toward the approaching car (+Z toward car = yaw 180).
|
||||
launch.CFrame = CFrame.new(rampX, 5, -zStart) * CFrame.Angles(0, math.rad(180), 0)
|
||||
launch.Color = Color3.fromRGB(50, 50, 62)
|
||||
launch.Material = Enum.Material.Metal
|
||||
launch.TopSurface = Enum.SurfaceType.Smooth
|
||||
launch.Parent = folder
|
||||
|
||||
local edge = part({
|
||||
Name = "RampEdgeGlow",
|
||||
Size = Vector3.new(ROAD_WIDTH / 2 - 4, 0.8, 1),
|
||||
Position = Vector3.new(rampX, 10.4, -zStart - 24),
|
||||
Color = NEON_PINK,
|
||||
local tx, tz = -HX - 220, 0
|
||||
local orange = Color3.fromRGB(235, 100, 40)
|
||||
local levels = {
|
||||
{ w = 64, h = 50, c = orange },
|
||||
{ w = 44, h = 45, c = Color3.fromRGB(240, 240, 245) },
|
||||
{ w = 28, h = 45, c = orange },
|
||||
{ w = 15, h = 40, c = Color3.fromRGB(240, 240, 245) },
|
||||
}
|
||||
local y = 0
|
||||
for li, lv in ipairs(levels) do
|
||||
local sec = part({
|
||||
Name = "TowerSection" .. li,
|
||||
Size = Vector3.new(lv.w, lv.h, lv.w),
|
||||
CFrame = CFrame.new(tx, y + lv.h / 2, tz),
|
||||
Color = lv.c,
|
||||
Material = Enum.Material.Metal,
|
||||
})
|
||||
sec.Parent = folder
|
||||
y += lv.h
|
||||
end
|
||||
-- Observation decks + antenna
|
||||
local deck1 = part({
|
||||
Name = "MainDeck",
|
||||
Size = Vector3.new(36, 8, 36),
|
||||
CFrame = CFrame.new(tx, 98, tz),
|
||||
Color = Color3.fromRGB(250, 245, 235),
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
edge.Parent = folder
|
||||
|
||||
-- Landing ramp: mirrored wedge further down so clean jumps land smooth.
|
||||
local landing = Instance.new("WedgePart")
|
||||
landing.Anchored = true
|
||||
landing.Name = "RampLanding"
|
||||
landing.Size = Vector3.new(ROAD_WIDTH / 2 - 4, 10, 60)
|
||||
landing.CFrame = CFrame.new(rampX, 5, -(zStart + 150))
|
||||
landing.Color = Color3.fromRGB(50, 50, 62)
|
||||
landing.Material = Enum.Material.Metal
|
||||
landing.TopSurface = Enum.SurfaceType.Smooth
|
||||
landing.Parent = folder
|
||||
deck1.Parent = folder
|
||||
local deck2 = part({
|
||||
Name = "TopDeck",
|
||||
Size = Vector3.new(20, 6, 20),
|
||||
CFrame = CFrame.new(tx, 152, tz),
|
||||
Color = Color3.fromRGB(250, 245, 235),
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
deck2.Parent = folder
|
||||
local antenna = part({
|
||||
Name = "Antenna",
|
||||
Size = Vector3.new(2, 40, 2),
|
||||
CFrame = CFrame.new(tx, 200, tz),
|
||||
Color = orange,
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
antenna.Parent = folder
|
||||
end
|
||||
|
||||
-- Neon skyline: procedural building blocks flanking the route -------------------
|
||||
local function buildSkyline(map: Folder, totalLength: number)
|
||||
-- Tatsumi-style overlook pad (visual scenery, NE corner outside) ------------------
|
||||
local function buildTatsumi(map: Folder)
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "TatsumiPA"
|
||||
folder.Parent = map
|
||||
local px, pz = HX - 380, -HZ - 120
|
||||
local pad = part({
|
||||
Name = "TatsumiPad",
|
||||
Size = Vector3.new(160, 2, 80),
|
||||
CFrame = CFrame.new(px, 0, pz),
|
||||
Color = Color3.fromRGB(34, 34, 40),
|
||||
Material = Enum.Material.Asphalt,
|
||||
})
|
||||
pad.Parent = folder
|
||||
for i = 0, 4 do
|
||||
local lamp = part({
|
||||
Name = "TatsumiLamp",
|
||||
Size = Vector3.new(1, 12, 1),
|
||||
CFrame = CFrame.new(px - 70 + i * 35, 6, pz - 30),
|
||||
Color = Color3.fromRGB(255, 240, 200),
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
lamp.Parent = folder
|
||||
end
|
||||
makeSign("辰巳PA TATSUMI PA", CFrame.new(px, 11, pz + 30), folder)
|
||||
end
|
||||
|
||||
-- Neon skyline: towers inside the loop (city core) and outside ---------------------
|
||||
local function buildSkyline(map: Folder)
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Skyline"
|
||||
folder.Parent = map
|
||||
|
||||
local accents = { NEON_PINK, NEON_BLUE, NEON_TEAL, Color3.fromRGB(200, 120, 255) }
|
||||
|
||||
for _, side in ipairs({ -1, 1 }) do
|
||||
local n = 0
|
||||
local z = -80
|
||||
while z > -totalLength do
|
||||
n += 1
|
||||
local seed = side * 1000 + n
|
||||
local width = 40 + det(seed) * 60
|
||||
local depth = 40 + det(seed + 1) * 60
|
||||
local height = 40 + det(seed + 2) * 160
|
||||
local gap = 30 + det(seed + 3) * 70
|
||||
local x = side * (ROAD_WIDTH / 2 + 50 + det(seed + 4) * 90 + width / 2)
|
||||
local function tower(seed: number, x: number, z: number)
|
||||
local width = 40 + det(seed) * 50
|
||||
local depth = 40 + det(seed + 1) * 50
|
||||
local height = 50 + det(seed + 2) * 170
|
||||
local t = part({
|
||||
Name = "Tower",
|
||||
Size = Vector3.new(width, height, depth),
|
||||
CFrame = CFrame.new(x, height / 2 - 10, z),
|
||||
Color = Color3.fromRGB(16, 16, 24),
|
||||
Material = Enum.Material.Slate,
|
||||
})
|
||||
t.Parent = folder
|
||||
local accent = accents[(math.floor(seed) % #accents) + 1]
|
||||
local band = part({
|
||||
Name = "TowerBand",
|
||||
Size = Vector3.new(width + 0.4, 2 + det(seed + 5) * 4, depth + 0.4),
|
||||
CFrame = CFrame.new(x, height * (0.35 + det(seed + 6) * 0.4), z),
|
||||
Color = accent,
|
||||
Material = Enum.Material.Neon,
|
||||
Transparency = 0.2,
|
||||
})
|
||||
band.Parent = folder
|
||||
local roof = part({
|
||||
Name = "TowerRoof",
|
||||
Size = Vector3.new(width * 0.3, 1.5, depth * 0.3),
|
||||
CFrame = CFrame.new(x, height - 9, z),
|
||||
Color = accent,
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
roof.Parent = folder
|
||||
end
|
||||
|
||||
local tower = part({
|
||||
Name = "Tower",
|
||||
Size = Vector3.new(width, height, depth),
|
||||
Position = Vector3.new(x, height / 2 - 10, z - depth / 2),
|
||||
Color = Color3.fromRGB(16, 16, 24),
|
||||
Material = Enum.Material.Slate,
|
||||
})
|
||||
tower.Parent = folder
|
||||
|
||||
-- Lit window band + rooftop accent (2 parts per tower, cheap).
|
||||
local accent = accents[(n % #accents) + 1]
|
||||
local band = part({
|
||||
Name = "TowerBand",
|
||||
Size = Vector3.new(width + 0.4, 2 + det(seed + 5) * 4, depth + 0.4),
|
||||
Position = Vector3.new(x, height * (0.4 + det(seed + 6) * 0.4), z - depth / 2),
|
||||
Color = accent,
|
||||
Material = Enum.Material.Neon,
|
||||
Transparency = 0.2,
|
||||
})
|
||||
band.Parent = folder
|
||||
|
||||
local roof = part({
|
||||
Name = "TowerRoof",
|
||||
Size = Vector3.new(width * 0.3, 1.5, depth * 0.3),
|
||||
Position = Vector3.new(x, height - 9, z - depth / 2),
|
||||
Color = accent,
|
||||
Material = Enum.Material.Neon,
|
||||
})
|
||||
roof.Parent = folder
|
||||
|
||||
z = z - depth - gap
|
||||
-- City core inside the loop
|
||||
local seed = 1
|
||||
for gx = -2, 2 do
|
||||
for gz = -1, 1 do
|
||||
seed += 7
|
||||
local x = gx * 300 + (det(seed) - 0.5) * 120
|
||||
local z = gz * 300 + (det(seed + 3) - 0.5) * 120
|
||||
tower(seed, x, z)
|
||||
end
|
||||
end
|
||||
-- Outer ring (skip landmark zones: Odaiba east, Tokyo Tower west-center,
|
||||
-- Daikoku south-center)
|
||||
for i = 1, 26 do
|
||||
seed += 11
|
||||
local ang = (i / 26) * math.pi * 2
|
||||
local rad = 1750 + det(seed + 4) * 350
|
||||
local x = math.cos(ang) * rad
|
||||
local z = math.sin(ang) * rad * 0.75
|
||||
local skip = (x > HX and math.abs(z) < 620) -- Odaiba
|
||||
or (x < -HX and math.abs(z) < 260) -- Tokyo Tower
|
||||
or (z > HZ and math.abs(x) < 420) -- Daikoku
|
||||
if not skip then
|
||||
tower(seed, x, z)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -288,61 +658,46 @@ function MapBuilder.build(): Folder
|
||||
local map = Instance.new("Folder")
|
||||
map.Name = "Map"
|
||||
|
||||
local roadFolder = Instance.new("Folder")
|
||||
roadFolder.Name = "Expressway"
|
||||
roadFolder.Parent = map
|
||||
local samples = genSamples()
|
||||
buildLoop(map, samples)
|
||||
buildDaikoku(map)
|
||||
buildRainbowBridge(map)
|
||||
buildOdaiba(map)
|
||||
buildTokyoTower(map)
|
||||
buildTatsumi(map)
|
||||
buildSkyline(map)
|
||||
|
||||
local totalLength = SEGMENT_LENGTH * NUM_SEGMENTS
|
||||
|
||||
buildRoad(roadFolder)
|
||||
buildBarriers(map, totalLength)
|
||||
buildGantries(map)
|
||||
buildTunnel(map)
|
||||
buildRamps(map)
|
||||
buildSkyline(map, totalLength)
|
||||
|
||||
-- Spawn pad (where cars appear) at the start ------------------------------
|
||||
local pad = part({
|
||||
Name = "SpawnPad",
|
||||
Size = Vector3.new(ROAD_WIDTH - 6, 1, 40),
|
||||
Position = Vector3.new(0, 1.5, -20),
|
||||
Color = Color3.fromRGB(20, 220, 120),
|
||||
Material = Enum.Material.Neon,
|
||||
Transparency = 0.4,
|
||||
})
|
||||
pad.Parent = map
|
||||
|
||||
-- Finish line at the far end ----------------------------------------------
|
||||
-- Finish line across the south straight just past the Daikoku exit --------
|
||||
local finish = part({
|
||||
Name = "FinishLine",
|
||||
Size = Vector3.new(ROAD_WIDTH, 1, 6),
|
||||
Position = Vector3.new(0, 1.5, -totalLength + 30),
|
||||
CFrame = CFrame.new(420, 1.5, HZ),
|
||||
Color = Color3.fromRGB(240, 240, 240),
|
||||
Material = Enum.Material.Neon,
|
||||
Transparency = 0.3,
|
||||
})
|
||||
finish.Parent = map
|
||||
|
||||
-- End wall so you can't sail off the far edge ------------------------------
|
||||
local endWall = part({
|
||||
Name = "KillWall_End",
|
||||
Size = Vector3.new(ROAD_WIDTH + 8, WALL_HEIGHT, 2),
|
||||
Position = Vector3.new(0, WALL_HEIGHT / 2, -totalLength - 1),
|
||||
Transparency = 1,
|
||||
CanCollide = true,
|
||||
})
|
||||
endWall.Parent = map
|
||||
|
||||
-- A floor far below to catch anything that clips through (safety net) ------
|
||||
-- Safety net floor under everything ----------------------------------------
|
||||
local floor = part({
|
||||
Name = "SafetyFloor",
|
||||
Size = Vector3.new(ROAD_WIDTH * 4, 2, totalLength + 400),
|
||||
Position = Vector3.new(0, -60, -totalLength / 2),
|
||||
Size = Vector3.new((HX + 700) * 2, 2, (HZ + 700) * 2),
|
||||
CFrame = CFrame.new(0, -60, 0),
|
||||
Transparency = 1,
|
||||
CanCollide = true,
|
||||
})
|
||||
floor.Parent = map
|
||||
|
||||
-- Ground plane far below road level (the "bay" the expressway floats over)
|
||||
local ground = part({
|
||||
Name = "Ground",
|
||||
Size = Vector3.new((HX + 700) * 2, 2, (HZ + 700) * 2),
|
||||
CFrame = CFrame.new(0, -12, 0),
|
||||
Color = Color3.fromRGB(10, 12, 20),
|
||||
Material = Enum.Material.Slate,
|
||||
})
|
||||
ground.Parent = map
|
||||
|
||||
map.Parent = Workspace
|
||||
return map
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user