添加回合

*此內容是使用 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 生成位置的 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 生成位置的 Neutral 屬性設置為 true,這樣玩家無論其團隊狀態如何都可以在那裡生成。這意味著大廳成為玩家在從回合中斷開連接後唯一可以生成的地方。

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

在等待十秒鐘的休息後,Rounds 伺服器腳本然後通過重置每個人的分數並將他們分配到新團隊中再次開始循環。該範例重複這一循環回合過程,直到伺服器中沒有玩家。

現在玩家可以與自己的團隊一起生成到地圖中並進行完整的回合,下一部分將教你有關每個槍械行為背後的腳本。

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