重生 是在體驗中創建對象或角色的過程,而 重生 是在經歷了移除條件,例如角色生命值達到零或掉出地圖後,將對象或角色重新添加到體驗中的過程。兩個過程都很重要,因為它們確保玩家能夠加入您的體驗,並可以繼續玩以提高技能。
使用 樣本雷射標籤體驗 作為參考,本教學的此部分教你如何使用和自訂 Roblox 內置功能來處理生成和重生,包括關於腳本指引的指示:
- 配置生成位置,使玩家只能生成到他們隊團隊的生成區域。
- 當新玩家和他們的角色加入體驗時,添加他們到回合中。
- 自訂在玩家生成和重生時防止損壞的力場。
- 處理客戶端狀態,使遊戲在適當時間正確運作。
- 在回合結束後重生角色。
- 執行小、雜項行動,這些行動對設置遊戲和角色參數至關重要。
本節包含大量的腳本內容,但在創建體驗時,而不是一次從零開始寫所有內容,它鼓勵您利用現有組件、快速迭代並找出哪些系統需要自定義實現來匹配您的願景。完成此部分後,您將學習如何實裝回合制遊戲,該遊戲可追蹤點數、監視玩家狀態並顯示回合結果。
配置生成位置
如果您現在進行體驗測試,所有玩家將隨機生成在綠色團隊伍的生成區或粉紅色團隊伍的生成區的 對象。這會呈現一個遊戲問題,玩家可以在對手的力場消失後立即在每個生成區域內標記對方。
為了對抗這個問題,樣本雷射標籤體驗配置了兩個生成位置,將 Neutral 屬性設置為 false 來限制對方團隊的玩家在錯誤的生成區域生成,以及在 TeamColor 屬性設置相應的 Team.Color 值從 分配隊伍顏色 在教學的上一部分中:


當玩家加入體驗時, 服務器腳本服務 > 遊戲 > 回合 > 在地圖上生成玩家 檢查看有多少玩家已經在每個團隊伍中,然後返回最少數玩家的隊伍。
在地圖上生成玩家
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- 按從最小到最大的順序排序團隊
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- 返回最小的團隊
return teams[1]
end
一旦知道擁有最少玩家的團隊,就將玩家排在該團隊中,將其 Player.Neutral 屬性設為 false ,因此玩家只能生成和重生到自己團隊的重生位置,然後將其 PlayerState 設為 SelectingBlaster,這是您稍後在教學中會瞭解更多的內容。
在地圖上生成玩家
local function spawnPlayersInMap(players: { Player })
for _, player in players do
player.Team = getSmallestTeam()
player.Neutral = false
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
task.spawn(function()
player:LoadCharacter()
end)
end
end
如果你檢查 工作區 > 世界 > 地圖 > 重生 ,你可以看到地圖上還有一個重生位置: 中立重生 。這個重生位置與其他重生位置不同,因為它沒有設置 TeamColor 屬性設置到體驗中的兩個團隊之一;相反,這個重生位置有一個 Neutral 屬性,它會根據是否有回合啟用而變化。
例如,如果回合已啟用,則 Neutral 屬性設為 false 因此 spawnPlayersInMap 可將玩家排成隊伍並將他們生成到競技場。然而,如果回合未啟用,例如一輪與下一輪之間的時間,Neutral 屬性將設為 真實 ,因此玩家無論他們的隊伍狀態如何都可以在那裡生成。這個過程是使 中立 重生位置成為功能性大廳的原因。

要展示,如果你檢查 服務器腳本服務 > 遊戲 > 回合 > 在大廳生成玩家 ,在回合結束時運行,你可以看到每個玩家都被傳到players: { Player }中的指令碼:
- 將 PlayerState 變更為 InLobby 以移除玩家的爆破器和第一人稱視覺效果。
要了解中立生成區和每一回合的功能的更多信息,請參見添加回合教學的下一部分。
在大廳生成玩家
local function spawnPlayersInLobby(players: { Player })
for _, player in players do
player.Neutral = true
player:SetAttribute(PlayerAttribute.playerState, PlayerState.InLobby)
task.spawn(function()
player:LoadCharacter()
end)
end
end
連接新玩家
工作室中的 Luau 代碼通常是事件驅動的,意味著腳本會聆聽 Roblox 服務的事件,然後在回應中呼叫一個函數。例如,當將新玩家添加到多人遊戲體驗時,必須有一個事件來處理玩家成功連接所需的一切。在樣本雷射標籤體驗中,對應的事件是 Players.PlayerAdded:Connect 。
Players.PlayerAdded:Connect 是體驗中的多個腳本的一部分。如果您使用 Ctrl/Cmd+Shift+F 快捷方式並搜尋 Players.PlayerAdded:Connect,結果將提供一個很好的起點來理解體驗的初始設置。

要展示,開啟 伺服器腳本服務 > 設置人形 。區分 Player 和 Character 是理解此指令碼的關鍵:
- 玩家需要選擇爆破器並被添加到排行榜上。角色需要生成並接收爆破器。
SetupHumanoid 立即檢查玩家是否有角色(剛剛加入)或沒有(正在重生)。找到一個後,它會呼叫 onCharacterAdded() ,從角色那裡獲得 Humanoid 模型,並將其傳給 ServerScriptService > SetupHumanoid > setupHumanoidAsync 進行自定义。設定這些值之後,腳本會等待角色的生命值達到零。您將在本教學的此部分中瞭解更多關於重生的內容。
設定人形同步
local function setupHumanoidAsync(player: Player, humanoid: Humanoid)
humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Subject
humanoid.NameDisplayDistance = 1000
humanoid.HealthDisplayDistance = 1000
humanoid.NameOcclusion = Enum.NameOcclusion.OccludeAll
humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOn
humanoid.BreakJointsOnDeath = false
humanoid.Died:Wait()
onHumanoidDied(player, humanoid)
end
這個腳本的重要注意事項是屬性是完全可選的,這意味著如果你移除函數的前六行,體驗仍然正常運作。而不是功能需求,每個屬性都讓你能做出符合遊戲目標的設計決定。例如:
- 如果您想要角色名稱在更近的距離顯示,請減少 Humanoid.NameDisplayDistance 的值。
- 如果您只想將角色的生命值顯示在 100% 以下,請將 Humanoid.HealthDisplayType 設為 在受到傷害時顯示 。
- 如果您希望角色在生命值達到 0 時分裂,請將 Humanoid.BreakJointsOnDeath 設置為 真實 。
如果您變更這些屬性的值,很重要進行測試,以便您可以看到新設定的影響。您可以在 測試 標籤的 客戶端和伺服器 部分選擇至少兩個角色來重創玩家在多人遊戲環境中的體驗。

另一個 Players.PlayerAdded:Connect 事件的例子在 ServerScriptService > 玩家狀態處理器 。和上一個例子一樣,PlayerStateHandler立即檢查一個字符。如果玩家不在大廳,腳本將設置玩家屬性為 SelectingBlaster 狀態,這是玩家在生成到競技場後可以從兩種不同爆破器類型中選擇的初始狀態。此狀態還包括一個力場,可以防止玩家在選擇時受到傷害。
玩家狀態處理器
local function onPlayerAdded(player: Player)
player.CharacterAdded:Connect(function()
if not player.Neutral then
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
onPlayerStateChanged(player, PlayerState.SelectingBlaster)
end
end)
在 PlayerStateHandler 戰爭中有一個特定變量需要討論:attributeChangedConnectionByPlayer 。這個表儲存所有玩家和他們的 Connections 到 GetAttributeChangedSignal 。將此連線存儲在表中的原因是為了當玩家離開體驗時,PlayerStateHandler 可以 斷開 它。這個過程可以作為一種記憶管理來防止連線數量隨著時間增長而不斷增加。
玩家狀態處理器
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- 處理所有未來對玩家狀態的更新
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- 當玩家離開時,從特性變更連線中斷
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
end
你可以看到兩個連接的功能在 onPlayerAdded() 呼叫 onPlayerStateChanged() 。在玩家分配到團隊後的初始設置期間,onPlayerAdded()將PlayerState設為SelectingBlaster,因此第一個if聲明評為為假並禁用BlasterState。在教學的後半部分 實現爆破器 部分,您將學到更多關於此過程的細節。
玩家狀態處理器
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- 爆破狀態只有當玩家狀態為「正在遊玩」時才是「準備好」
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- 當玩家開始遊戲時,預定摧毀力場邏輯
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
end
如果你添加斷點或甚至只是一個 聲明,你可以看到在整個體驗過程中 被頻繁地呼叫:例如在初始設置回合時,將自己設置在主代碼路徑上,玩家選擇爆破器後,玩家返回大廳或中立重生位置。此外,玩家選擇爆破器之後, 服務器腳本服務 > 爆破器選擇處理器 將PlayerState為Playing,並且PlayerStateHandler最終可以通過呼叫scheduleDestroyForceField()。
自訂力場
而不是使用自訂實現,樣本雷射標籤體驗使用 Studio 的內置 ForceField 類來防止玩家在選擇他們的爆破器時受到傷害。這樣可以確保玩家僅需要擁有一個力場生成位置,並包含擁有 SpawnLocation.Duration 屬性的生成位置,其值大於 0 的值。樣本使用隨意值 9,999 啟用力場,然後在 ReplicatedStorage > ForceFieldClientVisuals 中手動處理實際持續時間程序。
與 setupHumanoidAsync 類似,ForceFieldClientVisuals 中的大多數線是可選的。例如,如果你像下面的腳本一樣評論函數的內容,體驗將使用默認的閃光力場而不是六角形腳本在 StarterGui > ForceFieldGui 中。
在ForceFieldClientVisuals中評論屬性
local function onCharacterAddedAsync(character: Model)
-- 本地力場 = 角色:等待孩子("力場", 3)
-- 如果不是 forceField 則
-- 傳回回
-- 結束
-- forceField.Visible = 否
-- localPlayer.PlayerGui:等待兒童("力場GUI")啟用=真
-- forceField.Destroying:等待()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = 否
end
因為自訂力場是圖形介面,而不是新的 ,所以 腳本只影響每個玩家的第一人稱視覺,而不是當玩家看到其他玩家時的第三人稱視覺。第三人稱視覺仍保留預設 Roblox 外觀。有關修改力場的更多信息,請參閱 ForceField.Visible 。


力場有用,因為它們為玩家提供足夠的時間來在重生和重生之間進行,而不需要擔心敵方玩家,但最終需要消失以進行主要的雷射標籤遊遊玩操作處理力場移除的腳本位於 ReplicatedStorage > scheduleDestroyForceField ,並檢查三個獨特條件:
- 玩家選擇爆破器之後,力場需要存在足夠長的時間,讓玩家適應環境。
- 在這段適應期間,力場不能是優勢,因此當玩家發射爆破器時,它們需要消失的那一刻。
- 當玩家重設角色時,力場需要在爆炸前或力場時間到達之前消失。
每次在 scheduleDestroyForceField 腳本呼叫 endForceField() 檢查這些條件。
排程摧毀力場
-- 如果玩家爆炸,結束力場
local blasterStateAttribute = getBlasterStateAttribute()
attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function()
local currentBlasterState = player:GetAttribute(blasterStateAttribute)
if currentBlasterState == BlasterState.Blasting then
endForceField()
end
end)
-- 如果玩家重置,結束力場
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- 在 8 秒後結束力場
task.delay(MAX_FORCE_FIELD_TIME, endForceField)
endForceField() 包含一個似乎奇怪的 if 聲明,包圍著 forceFieldEnded 的 boolean。因為檢查是依序運行的,所以腳本可以召叫 endForceField() 功能兩次或甚至三次。forceFieldEnded 布林確保函數只試一次摧毀力場。
排程摧毀力場
local function endForceField()
if forceFieldEnded then
return
end
forceFieldEnded = true
attributeChangedConnection:Disconnect()
characterRespawnedConnection:Disconnect()
destroyForceField(player)
end
處理客戶狀態
雖然本節的大部分聚焦在 ServerScriptService > PlayerStateHandler 上,但在 ReplicatedStorage 中也有同名的另一個腳本。分割的原因是客戶端-伺服器架構:
客戶端需要理解玩家狀態資訊,以便它能在實時回應中適當地顯示正確的使用者介面元素,或啟用玩家移動和爆破。
伺服器需要所有相同的資訊,以便它能防止漏洞。例如,伺服器也需要玩家狀態來執行像生成和裝備角色、禁用力場和顯示排行榜等操作。這就是為什麼這個腳本在 ReplicatedStorage 中,而不是純粹的客戶端位置。
要查看此核心邏輯,請在 ReplicatedStorage > PlayerStateHandler 中查看以下腳本,該腳本驗證用戶當前狀態,然後呼叫對應狀態的相應功能來處理該狀態。
玩家狀態處理器
local function onPlayerStateChanged(newPlayerState: string)
if newPlayerState == PlayerState.SelectingBlaster then
onSelectingBlaster()
elseif newPlayerState == PlayerState.Playing then
onPlaying()
elseif newPlayerState == PlayerState.TaggedOut then
onTaggedOut()
elseif newPlayerState == PlayerState.InLobby then
onInLobby()
else
warn(`Invalid player state ({newPlayerState})`)
end
end
所有事件回應在此腳本中被邏輯地組合在一起,因為它們需要啟用或禁用玩家控控制、相機移動和哪個使用者介面層是可見的相同行為。例如,在爆破器選擇期間,玩家需要既不可移移動工具又不可被攻擊。伺服器已經處理力場,但客戶端處理移動。為了說明,如果您檢查 onSelectingBlaster() 功能的邏輯,您可以看到客戶端在他們選擇爆破器時禁用玩家移動。
玩家狀態處理器
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
onPlaying() 功能相同簡單。它啟用移動,切換到主頭部顯示(HUD),啟用爆破器,並呼叫與伺服器相同的力場功能。
玩家狀態處理器
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
end
重生角色
樣本雷射標籤體驗會將重生角色通過 onTaggedOut() 狀態回到 ReplicatedStorage > 玩家狀態處理器 的回合。像 onSelectingBlaster() 和 onPlaying() 狀態一樣,onTaggedOut() 根據 playerState 屬性的變化啟動獨特的行為。特別是,它停用玩家移動、呈現重生介面,並停用爆破器。
玩家狀態處理器
local function onTaggedOut()
-- 在標記時停用控件
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- 在標記時停用爆破器
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
如果您想測試此行為,您可以按下 Esc , 導航到 設定 標籤,然後單擊 重設角色 按鈕。注意,當您啟動重生屏幕時,您無法移動工具、旋轉相攝影機或爆炸您的爆炸器。


要注意的是,這個腳本並不會實際重生角色,只是停止它們的行動並向玩家提供視覺反饋,告訴他們 服務器 正在重生他們的角色。為了展示,如果您查看 ServerScriptService > SetupHumanoid > setupHumanoidAsync > onHumanoidDied ,腳本將設置 PlayerState 到 TaggedOut (基本上通知 ReplicatedStorage > PlayerStateHandler ),並添加一些視覺指標。重生的實際邏輯是內建 Roblox 行為。
當玩家重生回輪次時,他們會根據 SpawnLocation.TeamColor 屬性重生在他們的團隊伍生成位置。若要自訂重生時間,您可以將下列行加到 SetupHumanoid 的頂部。要了解更多關於此技術的信息,請參閱 Players.RespawnTime 。
設定人形化身
local Players = game:GetService("Players")Players.RespawnTime = 10 -- new line, in seconds
其他設定
作為初始設定的一部分,樣本雷射標籤體驗也會執行一些小但關鍵的步驟:
體驗包括一個名為 StarterPlayer > StarterCharacterScripts > 生命值 的空白腳本,可禁用預設 Roblox 生命值重生。要了解此屬性性的行為說明,請參見 Humanoid.Health 。
體驗使用第一人稱攝影機,通過設置 StarterPlayer.CameraMode.LockFirstPerson 屬性來實現。請注意,如果您想讓使用者在第一人稱和第三人稱攝影機之間切換,您必須通過程式變更屬性,而不是單純在 Studio 設置一次,並修改控制和介面來抵消視角變更。
現在玩家可以生成、選擇爆破器,並從第一人稱視檢視瞄準它,下一個部分教你關於創建回合遊玩的腳本。