结束匹配

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

匹配可以在计时器耗尽或单个玩家被留下等几种情况下结束。

管理被击败的玩家

现在,被击败的玩家在竞技场重生。相反,将他们发送回大厅等待下一场比匹配。

  1. 在 PlayerManager 中,创建一个名为 respawnPlayerInLobby() 的本地函数。在该函数中,执行以关注中/正在关注操作:
  • 将玩家的重生位置属性设置为 lobbySpawn。
  • 使用 Player:LoadCharacter() 重新加载角色。请记住,LoadCharacter() 在他们重生点生时创建玩家,移除他们拥有的任何工具。

使用匿名函数

匿名函数在特定情况下常用于高级脚本。在定义它们之前,了解您的脚本可能遇到的情况很重要。

考虑这个:要将被击败的玩家传送到大厅,重生函数必须连接到玩家的死亡事件。不过有一个问题。如果你像过去一样连接事件,那么无法从死亡事件中获取玩家的名称。脚本需要该名称来从跟踪活跃玩家的表中删除被击败的玩家。

要获取导致死亡事件的玩家名称,请使用匿名函数。 匿名函数 没有名称,可以直接在Connect()创建,而不是单独创建。

下面是一个示例匿名函数,显示了语法。


myPlayer.Died:Connect(function()
print(player)
end)

编写死亡事件

对于这个功能,每当玩家的角色死亡时,它们都会触发重生他们的功能。

  1. 要获得玩家死亡事件的访问权限,请在 PlayerManager > preparePlayer() 中添加一个变量为玩家的人形。


    local function preparePlayer(player, whichSpawn)
    player.RespawnLocation = whichSpawn
    player:LoadCharacter()
    local character = player.Character or player.CharacterAdded:Wait()
    local sword = playerWeapon:Clone()
    sword.Parent = character
    local humanoid = character:WaitForChild("Humanoid")
    end
  2. 创建一个匿名函数,连接到死亡事件。先输入 humanoid.Died:Connect() 。然后, 内部 Connect(),输入function(),按Enter自动完成end,然后删除额外的括号。


    local humanoid = character:WaitForChild("Humanoid")
    humanoid.Died:Connect(function()
    end)
  3. 在匿名函数中,调用 respawnPlayerInLobby() . 传递 player ,一个变量,存储玩家准备进入匹配赛的准备。


    local humanoid = character:WaitForChild("Humanoid")
    humanoid.Died:Connect(function()
    respawnPlayerInLobby(player)
    end)
  4. 启动一个 服务器 并进行比匹配。测试玩家死亡时,他们在大厅重生。要击败玩家,走到他们身边并使用你的武器,直到你确认他们在大厅重生为止。

    攻击玩家2
    >

    重生后的大厅中的玩家2
    >

注意,如果需要,也有一些不同的测试方法。

  • 要杀死一个玩家,在服务器 > 输出窗口 > 命令栏中,复制并粘贴:workspace.Player1.Humanoid.Health = 0。按Enter执行命令。要删除其他玩家,请使用 Player2、Player3 等
  • 将游戏设置 > matchDuration 设置为更长时间可以给你更多时间来找到并取出所有玩家。
  • 要进行更快的测试,将游戏设置 > minimumPlayers 更改为更小的数字,例如 2。

结束游戏

现在击败的玩家重生了,开始工作以结束游戏。记得创建匹配结束事件吗?当计时器耗尽或找到胜利者时,将触发该事件。

要告诉其他脚本哪个条件结束了游戏,创建一个包含变量 TimerUpFoundWinner 的表。当匹配结束事件发生时,它会传递一个变量,以便其他脚本可以回应。

  1. 在游戏设置中,创建一个名为 endStates 的空模块表。


    local GameSettings = {}
    -- 游戏变量
    GameSettings.intermissionDuration = 5
    GameSettings.matchDuration = 10
    GameSettings.minimumPlayers = 2
    GameSettings.transitionTime = 5
    -- 游戏可能结束的方式。
    GameSettings.endStates = {
    }
    return GameSettings
  2. 创建两个变量名为 TimerUpFoundWinner 。将每个设置为与名称匹配的字符串。这些字符串将用于测试代验证码。


GameSettings.endStates = {
TimerUp = "TimerUp",
FoundWinner = "FoundWinner"
}

以计时器结束

当计时器结束时,发射匹配结束事件,并发送匹配的结束状态变量。这样,其他正在听该事件的脚本可以根据需要进行回应。请记住,在事件发射信号时,也可以发送由收听脚本接收的数据,例如 TimerUpFoundWinner

  1. 游戏管理器 中,在 while 真实循环中,找到线 matchEnd.Event:Wait() 。在线的开头添加 local endState =


    while true do
    displayManager.updateStatus("Waiting for Players")
    repeat
    task.wait(gameSettings.intermissionDuration)
    until #Players:GetPlayers() >= gameSettings.minimumPlayers
    displayManager.updateStatus("Get ready!")
    task.wait(gameSettings.transitionTime)
    matchManager.prepareGame()
    local endState = matchEnd.Event:Wait()
    end
  2. 要确认收到正确的状态,请添加包含 endState 的打印声明。


    while true do
    displayManager.updateStatus("Waiting for Players")
    repeat
    task.wait(gameSettings.intermissionDuration)
    until #Players:GetPlayers() >= gameSettings.minimumPlayers
    displayManager.updateStatus("Get ready!")
    task.wait(gameSettings.transitionTime)
    matchManager.prepareGame()
    local endState = matchEnd.Event:Wait()
    print("Game ended with: " .. endState)
    end
  3. 在匹配管理器中,找到 timeUp()移除 打印声明。然后,要发射匹配结束事件,请输入 matchEnd:Fire() 并传递 gameSettings.endStates.TimerUp


    -- 值
    local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
    local timeLeft = displayValues:WaitForChild("TimeLeft")
    -- 创建一个新的计时对象,用于跟踪比赛时间。
    local myTimer = timer.new()
    -- 本地函数
    local function timeUp()
    matchEnd:Fire(gameSettings.endStates.TimerUp)
    end
  4. 测试匹配。一旦计时器耗尽,检查打印声明包含在 TimerUp 变量中的字符串。

排除问题的提示

在这一点上,消息未显示,请尝试以下其中一个。

  • 检查任何地方都调用了终端状态变量,它们是否正确地写入,如这里:gameSettings.endStates.TimerUp
  • 请确保使用:(斑点运营商)与 取代点运营商,如在 中。

编写一个胜利的比匹配

接下来,脚本需要识别获胜玩家、移除失败玩家,并运行清理操作,例如停止计时器。如果一个玩家被留下,匹配也会结束。要查看是否满足 FoundWinner 条件,你需要一个检查表中跟踪玩家的数量的函数。

检查玩家数量

如果只有一个玩家剩下,匹配将结束。要检查这一点,脚本需要跟踪玩家在一轮中。一旦一个玩家被淘汰,就可以分配胜利者。

  1. 在 PlayerManager 中,定义以下变量:

    • 模块脚本文件夹
    • 游戏设置模块 - 用于访问终端状态变量
    • 事件文件夹和匹配结束事件 - 发射事件

    local PlayerManager = {}
    -- 服务
    local Players = game:GetService("Players")
    local ServerStorage = game:GetService("ServerStorage")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    -- 模块
    local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
    local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
    -- 事件
    local events = ServerStorage:WaitForChild("Events")
    local matchEnd = events:WaitForChild("MatchEnd")
  2. respawnPlayerInLobby() 之上,添加一个名为 checkPlayerCount() 的新本地函数。


    -- 玩家变量
    local activePlayers = {}
    local playerWeapon = ServerStorage.Weapon
    local function checkPlayerCount()
    end
    local function respawnPlayerInLobby(player)
  3. 在那个函数中,使用 then 句检查是否有胜利者。在那个句子中:

    • 检查 activePlayers 表的大小是否为 1。
    • 如果是这样,请发射 matchEnd 并传递 gameSettings.endStates.FoundWinner

    local function checkPlayerCount()
    if #activePlayers == 1 then
    matchEnd:Fire(gameSettings.endStates.FoundWinner)
    end
    end

移除一个玩家

除非玩家被移除,否则不能保持准确的计数。当玩家被击败时,通过从玩家表中删除他们来保持准确的玩家数量。然后,检查活跃玩家表的大小以确定是否有获胜者。

  1. checkPlayerCount() 下,创建一个名为 removeActivePlayer() 的新本地函数,其参数名为 player


    local function checkPlayerCount()
    if #activePlayers == 1 then
    matchEnd:Fire(gameSettings.endStates.FoundWinner)
    end
    end
    local function removeActivePlayer(player)
    end
  2. 要在 activePlayers 表中找到玩家,使用 for 循环进行循环。然后,添加一个 if 声明,如果找到了匹配名称的玩家进入函数,就会运行。


    local function removeActivePlayer(player)
    for playerKey, whichPlayer in activePlayers do
    if whichPlayer == player then
    end
    end
    end
  3. 要移除玩家,在 if 声明中:

    • 调用 table.remove() . 在括号内,传递 activePlayers , 要查看的表,和 playerKey - 要从表中删除的玩家。
    • playersLeft 对象的值设置为 #activePlayers .
    • 通过运行 checkPlayerCount() 检查获胜玩家。

    local function removeActivePlayer(player)
    for playerKey, whichPlayer in activePlayers do
    if whichPlayer == player then
    table.remove(activePlayers, playerKey)
    playersLeft.Value = #activePlayers
    checkPlayerCount()
    end
    end
    end

连接事件和测试

要使用刚刚创建的函数,请从连接到玩家 Died 事件的匿名函数中调用它。

  1. 找到 preparePlayer() . 在死亡事件连接的匿名函数中,调用 removeActivePlayer() . 然后,将玩家传递为参数。


    humanoid.Died:Connect(function()
    respawnPlayerInLobby(player)
    removeActivePlayer(player)
    end)
  2. 要查看是否找到了赢家,请启动 测试服务器 。当只剩下一个玩家时,你应该在输出窗口中看到 FoundWinner。

  3. 继续测试并让匹配结束。注意当新的匹配开始时,输出窗口中出现错误:

这个错误是因为计时器没有停止,这将在下一节中修复。

停止计时器

每当比赛以胜利的玩家结束时,计时器也应停止。为了在时间到之停止计时器,在匹配结束事件触发时让计时器停止。这是创建事件的好处之一。它们可以在多种情况下重复使用,来编写原因和效果关系的脚本。

  1. 在匹配管理器中,创建一个名为 stopTimer() 的新本地函数。内部,输入 myTimer:stop() 来停止计时器。


    -- 创建一个新的计时对象,用于跟踪比赛时间。
    local myTimer = timer.new()
    -- 本地函数
    local function stopTimer()
    myTimer:stop()
    end
    local function timeUp()
    matchEnd:Fire(gameSettings.endStates.TimerUp)
    end
  2. 要在比赛结束时停止计时器,将 matchEnd 事件连接到 stopTimer()


    -- 模块功能
    function MatchManager.prepareGame()
    playerManager.sendPlayersToMatch()
    matchStart:Fire()
    end
    matchStart.Event:Connect(startTimer)
    matchEnd.Event:Connect(stopTimer)
    return MatchManager
  3. 测试 以前的错误不再出现,启动一个 服务器 。消除所有但一名玩家,然后在比赛结束后等待几秒钟。

下一步

虽然两个赢得条件已经完成,但还有一些任务需要完成游戏循环。例实例,胜利的玩家永远不会被传送到大厅。在下一课中,你将显示比赛如何结束给玩家,并重置游戏,最终完成整个循环。

已完成的脚本

以下是完成的脚本来检查您的工作。

玩家管理器脚本


local PlayerManager = {}
-- 服务
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 模块
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
-- 事件
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")
-- 地图变量
local lobbySpawn = workspace.Lobby.StartSpawn
local arenaMap = workspace.Arena
local spawnLocations = arenaMap.SpawnLocations
-- 值
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local playersLeft = displayValues:WaitForChild("PlayersLeft")
-- 玩家变量
local activePlayers = {}
local playerWeapon = ServerStorage.Weapon
-- 本地函数
local function checkPlayerCount()
if #activePlayers == 1 then
matchEnd:Fire(gameSettings.endStates.FoundWinner)
end
end
local function removeActivePlayer(player)
for playerKey, whichPlayer in activePlayers do
if whichPlayer == player then
table.remove(activePlayers, playerKey)
playersLeft.Value = #activePlayers
checkPlayerCount()
end
end
end
local function respawnPlayerInLobby(player)
player.RespawnLocation = lobbySpawn
player:LoadCharacter()
end
local function onPlayerJoin(player)
player.RespawnLocation = lobbySpawn
end
local function preparePlayer(player, whichSpawn)
player.RespawnLocation = whichSpawn
player:LoadCharacter()
local character = player.Character or player.CharacterAdded:Wait()
local sword = playerWeapon:Clone()
sword.Parent = character
local humanoid = character:WaitForChild("Humanoid")
humanoid.Died:Connect(function()
respawnPlayerInLobby(player)
removeActivePlayer(player)
end)
end
-- 模块功能
function PlayerManager.sendPlayersToMatch()
local arenaSpawns = spawnLocations:GetChildren()
for playerKey, whichPlayer in Players:GetPlayers() do
table.insert(activePlayers, whichPlayer)
local spawnLocation = table.remove(arenaSpawns, 1)
preparePlayer(whichPlayer, spawnLocation)
end
playersLeft.Value = #activePlayers
end
-- 事件
Players.PlayerAdded:Connect(onPlayerJoin)
return PlayerManager

游戏设置脚本


local GameSettings = {}
-- 游戏变量
GameSettings.intermissionDuration = 5
GameSettings.matchDuration = 10
GameSettings.minimumPlayers = 2
GameSettings.transitionTime = 5
-- 游戏可能结束的方式。
GameSettings.endStates = {
TimerUp = "TimerUp",
FoundWinner = "FoundWinner"
}
return GameSettings

游戏管理器脚本


-- 服务
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
-- 模块脚本
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local matchManager = require(moduleScripts:WaitForChild("MatchManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local displayManager = require(moduleScripts:WaitForChild("DisplayManager"))
-- 事件
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")
while true do
displayManager.updateStatus("Waiting for Players")
repeat
task.wait(gameSettings.intermissionDuration)
until #Players:GetPlayers() >= gameSettings.minimumPlayers
displayManager.updateStatus("Get ready!")
task.wait(gameSettings.transitionTime)
matchManager.prepareGame()
local endState = matchEnd.Event:Wait()
print("Game ended with: " .. endState)
end

匹配管理器脚本


local MatchManager = {}
-- 服务
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 模块脚本
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local playerManager = require(moduleScripts:WaitForChild("PlayerManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local timer = require(moduleScripts:WaitForChild("Timer"))
-- 事件
local events = ServerStorage:WaitForChild("Events")
local matchStart = events:WaitForChild("MatchStart")
local matchEnd = events:WaitForChild("MatchEnd")
-- 值
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local timeLeft = displayValues:WaitForChild("TimeLeft")
-- 创建一个新的计时对象,用于跟踪比赛时间。
local myTimer = timer.new()
-- 本地函数
local function stopTimer()
myTimer:stop()
end
local function timeUp()
matchEnd:Fire(gameSettings.endStates.TimerUp)
end
local function startTimer()
print("Timer started")
myTimer:start(gameSettings.matchDuration)
myTimer.finished:Connect(timeUp)
while myTimer:isRunning() do
-- 添加 +1 可确保计时器显示结束为 1 而不是 0。
timeLeft.Value = (myTimer:getTimeLeft() + 1) // 1
-- 通过不设置等待时间,它提供了更准确的循环
task.wait()
end
end
-- 模块功能
function MatchManager.prepareGame()
playerManager.sendPlayersToMatch()
matchStart:Fire()
end
matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)
return MatchManager