添加輪次

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

添加輪次 讓您將遊戲玩法結構化為具有明確開始和結束點的階段,以便玩家能夠衡量自己的進度,並定期獲得平等競技的機會。這對於基於團隊的遊戲玩法尤為重要,因為它提供了根據該輪中隊伍成員來調整遊玩風格的機會。

示例激光標籤體驗 作為參考,這部分教程教您如何使用和自定義 Roblox 的內建功能來組織每一輪,包括有關腳本的指導:

  • 透過重置個人和團隊的分數來開始一輪,然後將玩家生成到他們的團隊出生區域。
  • 自定義在每個玩家屏幕頂部設置回合目標的變數。
  • 追踪玩家對他們團隊得分的貢獻。
  • 根據玩家的團隊在回合中是否獲勝來觸發獨特的 UI 畫面。
  • 通過斷開玩家的連接並將他們生成到中立大廳來結束一輪。

完成這部分後,您將學習如何實現準確且令人滿意的擠壓器行為。

開始循環

ServerScriptServiceGameplayRounds 負責處理實現輪次的大部分邏輯,並通過調用 startRoundLoopAsync() 函數來標記一個輪次循環的開始。在玩家加入大廳並等待排序進入團隊的同時,startRoundLoopAsync()ServerScriptServiceGameplayScoring 中調用 resetScores() 函數來重置排行榜和團隊的分數。

Scoring

function Scoring.resetScores()
for _, player in Players:GetPlayers() do
player.leaderstats.Points.Value = 0
end
for _, team in Teams:GetTeams() do
team:SetAttribute(GuiAttribute.teamPoints, 0)
end
end

現在每個人都從零分開始,startRoundLoopAsync() 隨後將 中立 出生位置的 Neutral 屬性設置為 false,以便只有與出生位置的 TeamColor 屬性相同的 Team.Color 屬性的玩家才能在那裡出生。因為出生位置的 TeamColor 屬性被設置為 白色 而不是範例的薄荷綠或肉桂粉的隊伍,這種配置在輪次進行時防止所有玩家在那裡出生或重生。

對於當前在大廳的玩家,startRoundLoopAsync() 將目前在體驗中的所有玩家傳遞給 ServerScriptServiceGameplayRoundsspawnPlayersInMap 中的 spawnPlayersInMap 函數,以便將每個玩家按照大約相同的玩家數量進行排序和平衡。

對於那些在大廳組成團隊後加入遊戲的新玩家,startRoundLoopAsync() 監聽 Players.PlayerAdded:Connect 事件,然後再次調用 spawnPlayersInMap 函數將他們添加到至少有一名玩家的團隊。要了解有關此過程的更多信息,請參見教程前一部分的 配置出生地點

Rounds

-- 在地圖中生成所有玩家
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- 當新玩家加入地圖時生成他們
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)

設定目標

現在每個玩家都在與隊友一起的競技場中,該體驗需要提供成功完成回合所需的指示。示例激光標籤體驗通過在每個玩家的屏幕頂部提供一個目標提示來解決此要求,為團隊需要做的事情提供了清晰的指導,以便獲勝。

儘管您可以在 UI 課程中了解有關如何配置和顯示 目標 UI 組件的更多信息,但本節重點是在回合開始時如何實現目標,並從設置每個隊伍需要完成回合的分數數量開始。

即使運行時的目標提示告訴玩家他們需要獲得三分才能獲勝,如果您檢查 StarterGuiHUDGui 中的提示,您會看到它實際上包含可配置的 "%d" 作為分數值。

"%d" 是一個佔位符字符串,您可以在任何時候增加或減少,以滿足您自己的遊戲需求,方法是更新 ReplicatedStorageTEAM_SCORE_LIMIT 中的 TEAM_SCORE_LIMIT 變數。例如,如果您將此數字設置為過高的 200,則該提示和團隊分數計數器將相應更新。

TEAM_SCORE_LIMIT

local TEAM_SCORE_LIMIT = 200 -- 更新行,請確保更改回來
return TEAM_SCORE_LIMIT

這個簡單的變數更新可以在運行時工作,因為當回合開始時,ReplicatedStorageHUDGuiSetupSetObjective 需要 TEAM_SCORE_LIMIT 模塊腳本,以便它可以在 UI 目標的 TextLabel 對象中替換佔位符字符串。

TEAM_SCORE_LIMIT

local TEAM_SCORE_LIMIT = require(ReplicatedStorage.TEAM_SCORE_LIMIT)
local function setObjective(gui: ScreenGui)
local bodyTextLabel = gui.Objective.ObjectiveDisplay.Body.BodyTextLabel
bodyTextLabel.Text = bodyTextLabel.Text:format(TEAM_SCORE_LIMIT)
end

追踪分數

現在玩家有了回合的目標,該體驗需要追蹤每個團隊的分數,直到他們完成目標。雖然 Teams 服務的預設行為會自動將每個玩家分組到他們的團隊下,並將每個玩家對他們團隊分數的貢獻加總,但在基於回合的遊戲中,將分數存儲和監控在單獨的位置是重要的,因為如果玩家得分後在回合結束之前離開,他們的貢獻會在他們從體驗中斷開連接後立即從排行榜中扣除。

為了確保不發生這種情況,並且對團隊目標的每一項貢獻都能得到保留,ReplicatedStorageHUDGuiSetupStartSyncingTeamPoints 將所有分數單獨存儲在 Teams 服務的 teamPoints 屬性下。隨著 teamPoints 的增長,這個模組腳本調用 startSyncingTeamPoints 函數來查找目標 UI 組件中的團隊計數器 Class.GuiObjects

當它找到 TeamACounterTeamBCounter 時,會獲取它們的 teamColor 屬性,該屬性與團隊出生區域相關:TeamACounter 顯示綠隊的分數,而 TeamBCounter 跟踪粉紅隊的分數。

StartSyncingTeamPoints

local function startSyncingTeamPoints(gui: ScreenGui)
for _, teamPointCounter in gui.Objective.TeamPointCounter:GetChildren() do
if not teamPointCounter:IsA("GuiObject") then
continue
end
local iconTeamColor = teamPointCounter:GetAttribute(GuiAttribute.teamColor)

然後,模組腳本調用其 getTeamFromTeamColor 函數,以證實 TeamACounter 的 薄荷 teamColor 屬性和 TeamBCounter 的 肉色粉紅 teamColor 屬性是否都與 Teams 服務下的相應 Team.Color 屬性匹配。如果匹配,則返回這兩支隊伍。

StartSyncingTeamPoints

local function getTeamFromTeamColor(teamColor: Color3): Team?
for _, team in Teams:GetTeams() do
if team.TeamColor == teamColor then
return team
end
end
return nil
end

當這種情況發生時,startSyncingTeamPoints 將兩個團隊計數器的 TextLabel 對象設置為其相應的 teamPoints 值,並在玩家通過將另一名玩家拖出對方團隊而得分時不斷更新這些計數器。

StartSyncingTeamPoints

teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function()
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)

到目前為止,本節的所有內容都集中在如何在玩家的屏幕上追踪分數,但重要的是要回顧處理在服務器上追踪分數的邏輯,以便它知道什麼時候一支隊伍達到目標,並贏得這輪比賽。如果您重新查看 ServerScriptServiceGameplayScoring,您會發現模組腳本首先創建了一個可綁定事件,每次玩家得分時都會觸發。

Scoring

local teamScoreChangedBindable = Instance.new("BindableEvent")
local Scoring = {
teamScoreChanged = teamScoreChangedBindable.Event,
}

然後它呼叫 incrementScore 函數,該函數執行以下操作:

  • 獲取玩家的團隊和他們在 Teams 服務中的當前 teamPoints 值,然後加一。
  • 獲取玩家在排行榜上的個人分數,並加一。
  • 觸發之前提到的可綁定事件,傳遞玩家的團隊和他們的分數。

這個過程有效地保持了客戶端和服務器對於玩家的個人分數和他們的團隊分數的一致性。

Scoring

function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `Player {player.Name} must be on a team to score a point, but has no team`)
local teamPoints = team:GetAttribute(GuiAttribute.teamPoints)
teamPoints += amount
team:SetAttribute(GuiAttribute.teamPoints, teamPoints)
local leaderstat = player.leaderstats.Points
leaderstat.Value += amount
teamScoreChangedBindable:Fire(team, teamPoints)
end

顯示結果

隨著玩家相互標記並為他們的團隊得分,ServerScriptServiceGameplayRounds 檢查得分的團隊是否達到回合目標。如果他們的團隊分數低於 ReplicatedStorageTEAM_SCORE_LIMIT 中的 TEAM_SCORE_LIMIT 變數,則服務器將繼續等待,直到其中一支隊伍再次得分。

然而,一旦某支隊伍的分數達到 TEAM_SCORE_LIMIT 變數,腳本將發射一個 roundWinnerRemote 事件實例,傳遞玩家的名字和他們的隊伍。

Rounds

-- 檢查在每次得分後回合是否結束
local team: Team
local score: number = 0
while score < TEAM_SCORE_LIMIT do
team, score = Scoring.teamScoreChanged:Wait()
end
-- 顯示獲勝團隊
for _, player in Players:GetPlayers() do
-- 發送玩家在回合結束時屬於哪個隊伍
-- 因為玩家的隊伍即將移除,因此客戶端
-- 將無法檢查自己的隊伍
roundWinnerRemote:FireClient(player, team, player.Team)
end

ReplicatedStorageRoundResultsGuiSetup 腳本在每個客戶端監聽這個 roundWinnerRemote 事件實例,以便它可以:

  • 顯示獨特的 StarterGuiRoundResultsGui UI 畫面,公告回合結果以及玩家是否在獲勝團隊上。
  • 播放勝利或失敗的音頻片段。

例如,如果某玩家位於得分獲勝點的球隊,他們會通過顯示勝利文本的 UI 畫面和播放愉悅的音效音頻片段來接收到多種形式的反饋。相對而言,如果某玩家不在得分獲勝點的隊伍中,他們會收到一個顯示失敗文本的 UI 畫面和播放不祥的音效音頻片段。

獲勝反饋
失敗反饋
RoundResultsGuiSetup

local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "回合結束!"
if localTeam then
-- 如果我們的隊伍獲勝,我們將顯示勝利!否則顯示失敗...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
end

重置團隊

與此同時,ServerScriptServiceGameplayRounds 驗證某個團隊是否達到輪次目標並觸發每個玩家的適當 UI 顯示的同時,它還通過將所有玩家從競技場轉移到大廳,將他們從回合中斷開連接。這開始了正式結束回合並重置兩支隊伍的過程。

使用 配置出生地點 中相同的邏輯,Rounds 然後將 中立 出生位置的 Neutral 屬性設置為 true,以便玩家可以在那裡出生,而不考慮他們的隊伍狀態。這意味著大廳將成為玩家在從回合中斷開連接後唯一可以生成的地方。

Rounds

-- 將所有人送到大廳
playerAddedConnection:Disconnect()
neutralSpawn.Neutral = true
spawnPlayersInLobby(Players:GetPlayers())

在等待十秒鐘的中場休息後,Rounds 服務器腳本將通過重置每個人的分數並將他們排序到新的隊伍中來重新啟動循環。這個示例將再次重複這個循環輪次過程,直到伺服器中沒有玩家為止。

現在玩家可以與自己的隊伍進入地圖並進行完整的一輪,下一部分將教您有關每個擠壓器行為背後的腳本。

©2026 Roblox Corporation、Roblox、Roblox 標誌及 Powering Imagination 是我們在美國及其他國家地區的部分註冊與未註冊商標。