Menu
CoddyTech

Vòng này nối tiếp vòng kia

Bài học 15 trên 21 trong khóa học Trò chơi Roblox: Tháp bất tận của Coddy.

Một vòng chơi chỉ là bản demo; trò chơi diễn ra hết vòng này đến vòng khác. Đặt phần đếm ngược ở sảnh, vòng chơi và phần đếm ngược của vòng chơi bên trong while true do, rồi thêm hàm endRound() để dọn dẹp khi hết giờ:

  • người chơi vẫn ở trên tháp sẽ quay về sảnh,
  • tháp và dung nham bị phá hủy để vòng tiếp theo dựng một tòa tháp mới thay thế.

Destroy xóa một đối tượng và mọi thứ bên trong nó, vì vậy phá hủy Model của tháp sẽ xóa tất cả các tầng cùng lúc. Vòng lặp không bao giờ kết thúc, nên nó nằm ở cuối cùng của script: mọi thứ sau vòng lặp sẽ không bao giờ chạy.

challenge icon

Thử thách

Trung bình

Chạy trò chơi qua từng vòng.

  1. Viết endRound(): đưa mọi người chơi chưa hoàn thành lên vị trí cao hơn lobbySpawn 4 studs, hủy tower và lava, rồi làm trống towerStages.
  2. Ở cuối, đặt thời gian đếm ngược ở sảnh, startRound() và thời gian đếm ngược của vòng vào trong while true do, kết thúc mỗi lượt bằng endRound().

Nhấn Chơi: khi hết giờ, bạn sẽ quay lại sảnh để chờ tòa tháp tiếp theo. Sau đó nhấn Kiểm tra.

Tự làm thử

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local Settings = require(game.ReplicatedStorage.Settings)

local stages = ServerStorage.Stages
local lobbySpawn = workspace.Lobby.SpawnLocation
local tower = nil
local towerStages = {}
local lava = nil
local finished = {}

print("Each tower has " .. Settings.StageCount .. " stages")

-- Mỗi người chơi có Wins trên bảng xếp hạng
Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local wins = Instance.new("IntValue")
	wins.Name = "Wins"
	wins.Parent = leaderstats
end)

-- Bảng thông báo trong sảnh
local gui = Instance.new("SurfaceGui")
gui.Parent = workspace.Lobby.Board

local boardLabel = Instance.new("TextLabel")
boardLabel.Name = "Status"
boardLabel.Size = UDim2.fromScale(1, 1)
boardLabel.BackgroundColor3 = Color3.fromRGB(25, 25, 35)
boardLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
boardLabel.TextScaled = true
boardLabel.Text = "Endless Tower"
boardLabel.Parent = gui

local function setBoard(text)
	boardLabel.Text = text
end

-- Chạm vào bất kỳ vật nào có thẻ Kill sẽ mất một mạng
local function killOnTouch(hit)
	local humanoid = hit.Parent:FindFirstChild("Humanoid")
	if humanoid then
		humanoid.Health = 0
	end
end

-- Bậc thang biến mất dần sau khi có người đáp xuống, rồi xuất hiện trở lại
local function makeVanish(part)
	part.Touched:Connect(function(hit)
		if part:GetAttribute("Gone") or not hit.Parent:FindFirstChild("Humanoid") then
			return
		end
		part:SetAttribute("Gone", true)
		task.wait(Settings.VanishDelay)
		part.Transparency = 0.8
		part.CanCollide = false
		task.wait(Settings.VanishTime)
		part.Transparency = 0
		part.CanCollide = true
		part:SetAttribute("Gone", nil)
	end)
end

-- Tia laser trượt đến vị trí lệch theo Slide rồi quay lại, lặp mãi
local function makeSlide(part)
	local info = TweenInfo.new(Settings.SlideTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, -1, true)
	local goal = { Position = part.Position + part:GetAttribute("Slide") }
	TweenService:Create(part, info, goal):Play()
end

-- Tấm bật đẩy người đáp xuống lên không trung
local function bounce(hit)
	local root = hit.Parent:FindFirstChild("HumanoidRootPart")
	if root then
		root.AssemblyLinearVelocity = Vector3.new(0, Settings.BouncePower, 0)
	end
end

-- Các vật xoay quay một chút ở mỗi khung hình
RunService.Heartbeat:Connect(function(dt)
	for _, spinner in CollectionService:GetTagged("Spinner") do
		if spinner:IsDescendantOf(workspace) then
			spinner:PivotTo(spinner:GetPivot() * CFrame.Angles(0, math.rad(Settings.SpinSpeed) * dt, 0))
		end
	end
end)

-- Kích hoạt mọi chướng ngại vật trong một tầng theo thẻ của chúng
local function setupStage(stage)
	for _, part in stage:GetDescendants() do
		if part:HasTag("Kill") then
			part.Touched:Connect(killOnTouch)
		end
		if part:HasTag("Vanish") then
			makeVanish(part)
		end
		if part:HasTag("Conveyor") then
			part.AssemblyLinearVelocity = part.CFrame.LookVector * Settings.BeltSpeed
		end
		if part:HasTag("Slider") then
			makeSlide(part)
		end
		if part:HasTag("Bounce") then
			part.Touched:Connect(bounce)
		end
	end
end

-- Chọn ngẫu nhiên một tầng
local function pickStage()
	local all = stages:GetChildren()
	return all[math.random(1, #all)]
end

-- Đặt bản sao của một mô hình lên đỉnh tháp; trả về bản sao và vị trí đỉnh mới
local function stack(template, bottom)
	local copy = template:Clone()
	local box, size = copy:GetBoundingBox()
	local lift = bottom - (box.Position.Y - size.Y / 2)
	copy:PivotTo(copy:GetPivot() + Vector3.new(0, lift, 0))
	copy.Parent = tower
	return copy, bottom + size.Y
end

-- Đến đỉnh tháp: thắng và quay về sảnh
local function reachTop(hit)
	local player = Players:GetPlayerFromCharacter(hit.Parent)
	if not player or finished[player] then
		return
	end
	finished[player] = true
	player.leaderstats.Wins.Value += 1
	print(player.Name .. " reached the top")
	hit.Parent:PivotTo(lobbySpawn.CFrame + Vector3.new(0, 4, 0))
end

-- Xây tháp mới với các tầng ngẫu nhiên, từ dưới lên
local function buildTower()
	tower = Instance.new("Model")
	tower.Name = "Tower"
	tower.Parent = workspace
	towerStages = {}
	local top = 0
	for i = 1, Settings.StageCount do
		local stage, newTop = stack(pickStage(), top)
		top = newTop
		setupStage(stage)
		towerStages[i] = stage
	end
	local finish = stack(ServerStorage.Finish, top)
	finish.FinishPad.Touched:Connect(reachTop)
	return top
end

-- Dung nham dâng từ mặt đất lên sát dưới đỉnh tháp
local function startLava(top)
	lava = Instance.new("Part")
	lava.Name = "Lava"
	lava.Anchored = true
	lava.CanCollide = false
	lava.Size = Vector3.new(30, 2, 30)
	lava.Position = Vector3.new(0, -2, 0)
	lava.Color = Color3.fromRGB(255, 90, 20)
	lava.Material = Enum.Material.Neon
	lava.Parent = workspace
	lava.Touched:Connect(killOnTouch)
	local info = TweenInfo.new(top / Settings.LavaSpeed, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, Settings.LavaDelay)
	TweenService:Create(lava, info, { Position = Vector3.new(0, top - 2, 0) }):Play()
end

local function startRound()
	finished = {}
	local top = buildTower()
	startLava(top)
	for _, player in Players:GetPlayers() do
		if player.Character then
			player.Character:PivotTo(towerStages[1].Start.CFrame + Vector3.new(0, 4, 0))
		end
	end
end

-- Một vòng chơi
for i = Settings.Intermission, 1, -1 do
	setBoard("Next tower in " .. i)
	task.wait(1)
end
startRound()
for i = Settings.RoundLength, 1, -1 do
	setBoard("Climb! " .. i .. " seconds left")
	task.wait(1)
end

-- Bài học 15: Thêm nội dung Round After Round vào đây
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Trò chơi Roblox: Tháp bất tận

Tự luyện tập: Playground Roblox