7.クリーンアップとリセット

ゲームの最後のフェーズであるクリーンアップとリセットをコーディングします。このフェーズのコードは、ゲームがインターミッションまでループし、次の試合が各プレイヤーにとって同じように開始されるようにします。

GUI の更新

クリーンアップとリセットを行う前に、DisplayManager を使用してゲームがどのように終了したかをプレーヤーに通知し、適切なメッセージを表示します。

勝者の名前を取得する

勝者がいる場合は、勝者の名前を取得することから始めます。以前のコードでは、activePlayers テーブルのサイズが 1 になったかどうかをチェックしていました。残りのプレイヤーの名前を取得するには、そのテーブルの最初のインデックスにある名前を返します。

  1. PlayerManager で、 getWinnerName() という名前の新しいモジュール関数を作成します。
function PlayerManager.getWinnerName()

end

-- Events
Players.PlayerAdded:Connect(onPlayerJoin)
  1. activePlayers[1] に何かが存在する場合に実行する if 文を追加します。以前はテーブル数をチェックしていましたが、プレイヤーのネットが切断されたり、ゲームから退出した可能性があります。
function PlayerManager.getWinnerName()
    if activePlayers[1] then
    end
end
  1. if 文では
  • 勝者を変数に格納します。
  • プレイヤーの名前を返します。
  • else の場合、エラー文字列を返します。
function PlayerManager.getWinnerName()
    if activePlayers[1] then
        local winningPlayer = activePlayers[1]
        return winningPlayer.Name
    else
        return "Error: No winning player found"
    end
end

終了ステータスの取得

モジュール関数を使用して、タイマーが終了したのか、プレイヤーが1人残っているのかの情報を取得します。そして、その情報を DisplayManager に送り、適切なメッセージをGUIで表示します。

  1. MatchManagerで、endState というパラメータを持つ getEndStatus() という新しいモジュール関数を作成します。送信されるメッセージを格納するために、statusToReturn という名前の空の変数を追加します。
function MatchManager.prepareGame()
    playerManager.sendPlayersToMatch()
    matchStart:Fire()
end

function MatchManager.getEndStatus(endState)
    local statusToReturn
end

matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)

return MatchManager
  1. if と elseif 文を使って statusToReturn の値を設定します。endState 変数を確認します。FoundWinner とTimerUp をチェックします。エラーチェックのため、最後にelseを入れます。
function MatchManager.getEndStatus(endState)
    local statusToReturn

    if endState == gameSettings.endStates.FoundWinner then

    elseif endState == gameSettings.endStates.TimerUp then

    else

    end
end
  1. 各条件について以下を追加してください。

【勝者がいた場合】

  • playerManager.getWinnerName() で勝者を取得します。
  • statusToReturn 変数に勝者を知らせるメッセージを代入します。

【タイマーアップした場合】

  • statusToReturn 変数に、時間切れを知らせるメッセージを代入します。

【それ以外】

  • ゲーム終了メッセージの取得に問題がある場合に備えて、エラーメッセージを代入します。
function MatchManager.getEndStatus(endState)
    local statusToReturn

    if endState == gameSettings.endStates.FoundWinner then
        local winnerName = playerManager.getWinnerName()
        statusToReturn = "Winner is : " .. winnerName
    elseif endState == gameSettings.endStates.TimerUp then
        statusToReturn = "Time ran out!"
    else
        statusToReturn = "Error found"
    end
end
  1. return statusToReturnと入力して、メッセージを送り返します。
function MatchManager.getEndStatus(endState)
    local statusToReturn

    if endState == gameSettings.endStates.FoundWinner then
        local winnerName = playerManager.getWinnerName()
        statusToReturn = "Winner is : " .. winnerName
    elseif endState == gameSettings.endStates.TimerUp then
        statusToReturn = "Time ran out!"
    else
        statusToReturn = "Error found"
    end

    return statusToReturn
end

表示とテスト

GameManager で更新されたメッセージを取得し、DisplayManager でプレイヤーに表示します。

  1. GameManager を開きます。while true doループの中で、最後の print 文を削除します。次に、endStatus という名前の変数を作成します。matchManager.getEndStatus() に endStatus をそのまま渡して呼び出します。
while true do
    displayManager.updateStatus("Waiting for Players")

    repeat
        wait(gameSettings.intermissionDuration)
    until Players.NumPlayers >= gameSettings.minimumPlayers

    displayManager.updateStatus("Get ready!")
    wait(gameSettings.transitionTime)

    matchManager.prepareGame()
    local endState = matchEnd.Event:Wait()

    local endStatus = matchManager.getEndStatus(endState)
end
  1. 返されたメッセージを GUI で表示するには、displayManager.updateStatus() を呼び出して endStatus を渡します。
    local endStatus = matchManager.getEndStatus(endState)
    displayManager.updateStatus(endStatus)
  1. プレイヤーにメッセージを見せるために、transitionTime を使用して待ち時間を追加します。
    local endStatus = matchManager.getEndStatus(endState)
    displayManager.updateStatus(endStatus)

    wait(gameSettings.transitionTime)
  1. テストサーバーを起動し、タイムアップと勝利プレイヤーの2つの条件で、以下のメッセージがプレイヤーに表示されることを確認してください。
タイムアップ
勝利プレイヤー

トラブルシューティング

このようにメッセージが表示されない場合は、以下のいずれかを試してください。

  • 終了メッセージが「Error Found」の場合、どの条件も成功しなかったことになります。MatchManager.getEndStatus() 内のコードとこのページの最後に掲載されているサンプルコードを照らし合わせてみてください。
  • 終了メッセージが表示されない場合は、wait(gameSettings.transitionTime) が displayManager を呼び出した後にあることを確認します。

新しい試合の開始

新しい試合を開始する前に、短い移行処理を行います。これにより、プレイヤーは終了メッセージを見る時間ができ、いきなりロビーに転送されることがなくなります。

移行が終了すると、残っているプレイヤーはアリーナから排除され、すべてのコードがリセットされます。これにより、プレイヤーはゲームがクリーンな状態で次の試合を開始することができます。

移行への対応

プレイヤーが移行状態に入ったら、武器を取り外します。

  1. PlayerManager の中で、ローカル関数を見つけます。removePlayerWeapon() コードをコピー&ペーストしてください。このコードは、プレイヤーの武器がアクティブに装備されているか、プレイヤーのバックパックにある場合に、個々のプレイヤーの武器を削除するものです。
local function removePlayerWeapon(whichPlayer)
    -- Check to see if a player exist in case they disconnected or left.
    if whichPlayer then
        local character = whichPlayer.Character

        -- If the player has it currently on their character
        if character:FindFirstChild("Weapon") then
            character.Weapon:Destroy()
        end
        -- If theplayer has the weapon in their backpack
        if whichPlayer.Backpack:FindFirstChild("Weapon") then
            whichPlayer.Backpack.Weapon:Destroy()
        end
    else
        print("No player to remove weapon")
    end
end
  1. removeAllWeapons() という名前の新しいモジュール関数を作成します。
function PlayerManager.removeAllWeapons()

end

-- Events
Players.PlayerAdded:Connect(onPlayerJoin)

return PlayerManager
  1. その関数の中で、pairs() を使ったforループを使い、activePlayers に含まれるアクティブプレイヤーを調べていきます。そのループの中でプレイヤーを引数とし、removePlayerWeapon() を呼び出します。
function PlayerManager.removeAllWeapons()
    for playerKey, whichPlayer in pairs(activePlayers) do
        removePlayerWeapon(whichPlayer)
    end
end

試合間の掃除

クリーンアップは、MatchManager の独自の機能になります。今のところ、クリーンアップは以前に作成された関数を使用してプレイヤーの武器を削除します。ゲームを拡張すると、試合中に変更されたマップをリセットする機能など、さらに追加できます。

  1. MatchManager を開きます。cleanupMatch() という名前の新しいモジュール関数を追加します。その関数で、playerManager.removeAllWeapons() を呼び出します。
function MatchManager.cleanupMatch()
    playerManager.removeAllWeapons()
end

matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)

return MatchManager
  1. 次に、クリーンアップ関数を呼び出します。GameManager を開き、while true do ループを見つけます。休憩タイムに入った際にプレイヤーから武器を外すために、最後の wait() の前に matchManager.cleanupMatch() を呼び出します。
while true do
    displayManager.updateStatus("Waiting for Players")

    repeat
        wait(gameSettings.intermissionDuration)
    until Players.NumPlayers >= gameSettings.minimumPlayers

    displayManager.updateStatus("Get ready!")
    wait(gameSettings.transitionTime)

    matchManager.prepareGame()
    local endState = matchEnd.Event:Wait()

    local endStatus = matchManager.getEndStatus(endState)
    displayManager.updateStatus(endStatus)

    matchManager.cleanupMatch()
    wait(gameSettings.transitionTime)
end
  1. テストサーバーを起動し、試合を実行します。タイマーが切れるのを待ち、休憩タイムに入った際にプレーヤーの武器が取り外されていることを確認します。
試合中
試合後

試合のリセット

試合終了後もアリーナにプレイヤーが残っているなど、いくつか気づいたことがあるかもしれません。試合がクリーンアップされたら、次にゲームをリセットします。これは、アリーナにいるプレイヤーをロビーに戻すことと、アクティブプレイヤーテーブルをクリアすることを含みます。リセットを行うことで、ゲームループを無限に走らせることができます。

まず、プレイヤーをロビーに戻す関数を開始します。

  1. PlayerManager で
    • resetPlayers() という名前のモジュール関数を作成します。
    • ActivePlayers を反復処理するには、 pairs() を使用して for ループを追加します。
    • ループ内でrespawnPlayerInLobby() を呼び出し、プレーヤーをパラメーターとして渡します。
function PlayerManager.resetPlayers()
    for playerKey, whichPlayer in pairs(activePlayers) do
        respawnPlayerInLobby(whichPlayer)
    end
end

-- Events
Players.PlayerAdded:Connect(onPlayerJoin)

return PlayerManager
  1. activePlayersテーブルに {} を代入することで空になります。空の配列(テーブル)にリセットするもっとも早い方法です。
function PlayerManager.resetPlayers()
    for playerKey, whichPlayer in pairs(activePlayers) do
        respawnPlayerInLobby(whichPlayer)
    end

    activePlayers = {}
end
  1. MatchManager を開きます。resetMatch() という名前の新しいモジュール関数を作成し playerManager.resetPlayers() を呼び出します。
function MatchManager.resetMatch()
    playerManager.resetPlayers()
end

matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)

return MatchManager
  1. GameManager に戻ります。while true do ループの最後で、matchManager.resetMatch() を呼び出します。
while true do
    displayManager.updateStatus("Waiting for Players")

    repeat
        wait(gameSettings.intermissionDuration)
    until Players.NumPlayers >= gameSettings.minimumPlayers

    displayManager.updateStatus("Get ready!")
    wait(gameSettings.transitionTime)

    matchManager.prepareGame()
    local endState = matchEnd.Event:Wait()

    local endStatus = matchManager.getEndStatus(endState)
    displayManager.updateStatus(endStatus)

    matchManager.cleanupMatch()
    wait(gameSettings.transitionTime)

    matchManager.resetMatch()
end
  1. テスト サーバーを起動し、試合を実行します。少なくとも 2回のゲームループをエラーなく実施できることを確認します。

完成したスクリプト

完成したスクリプトを掲載しますのでチェックしてみてください。

GameManager スクリプト

-- Services
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")

-- Module Scripts
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local matchManager = require(moduleScripts:WaitForChild("MatchManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local displayManager = require(moduleScripts:WaitForChild("DisplayManager"))

-- Events
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")

while true do
    displayManager.updateStatus("Waiting for Players")

    repeat
        wait(gameSettings.intermissionDuration)
    until Players.NumPlayers >= gameSettings.minimumPlayers

    displayManager.updateStatus("Get ready!")
    wait(gameSettings.transitionTime)

    matchManager.prepareGame()
    local endState = matchEnd.Event:Wait()

    local endStatus = matchManager.getEndStatus(endState)
    displayManager.updateStatus(endStatus)

    matchManager.cleanupMatch()
    wait(gameSettings.transitionTime)

    matchManager.resetMatch()
end

MatchManager スクリプト

local MatchManager = {}

-- Services
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Module Scripts
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local playerManager = require(moduleScripts:WaitForChild("PlayerManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local displayManager = require(moduleScripts:WaitForChild("DisplayManager"))
local timer = require(moduleScripts:WaitForChild("Timer"))

-- Events
local events = ServerStorage:WaitForChild("Events")
local matchStart = events:WaitForChild("MatchStart")
local matchEnd = events:WaitForChild("MatchEnd")

-- Values
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local timeLeft = displayValues:WaitForChild("TimeLeft")

-- Creates a new timer object to be used to keep track of match time.
local myTimer = timer.new()

-- Local Functions
local function stopTimer()
    myTimer:stop()
end

local function timeUp()
    matchEnd:Fire(gameSettings.endStates.TimerUp)
end

local function startTimer()
    myTimer:start(gameSettings.matchDuration)
    myTimer.finished:Connect(timeUp)

    while myTimer:isRunning() do
        -- Adding +1 makes sure the timer display ends at 1 instead of 0.
        timeLeft.Value = (math.floor(myTimer:getTimeLeft() + 1))
        -- By not setting the time for wait, it offers more accurate looping
        wait()
    end
end

-- Module Functions
function MatchManager.prepareGame()
    playerManager.sendPlayersToMatch()
    matchStart:Fire()
end

function MatchManager.getEndStatus(endState)
    local messageToReturn

    if endState == gameSettings.endStates.FoundWinner then
        local winnerName = playerManager.getWinnerName()
        messageToReturn = "Winner is : " .. winnerName
    elseif endState == gameSettings.endStates.TimerUp then
        messageToReturn = "Time ran out!"
    else
        messageToReturn = "Error found"
    end

    return messageToReturn
end

function MatchManager.cleanupMatch()
    playerManager.removeAllWeapons()
end

function MatchManager.resetMatch()
    playerManager.resetPlayers()
end

matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)

return MatchManager

PlayerManager スクリプト

local PlayerManager = {}

-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Modules
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))

-- Events
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")

-- Map Variables
local lobbySpawn = workspace.Lobby.StartSpawn
local arenaMap = workspace.Arena
local spawnLocations = arenaMap.SpawnLocations

-- Values
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local playersLeft = displayValues:WaitForChild("PlayersLeft")

-- Player Variables
local activePlayers = {}
local playerWeapon = ServerStorage.Weapon

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

local function removeActivePlayer(player)
    print("removing player")
    for playerKey, whichPlayer in pairs(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 preparePlayer(player, whichSpawn)
    player.RespawnLocation = whichSpawn
    player:LoadCharacter()

    local character = player.Character or player.CharacterAdded:Wait()
    -- Give the player a tool
    local sword = playerWeapon:Clone()
    sword.Parent = character

    local humanoid = character:WaitForChild("Humanoid")

    humanoid.Died:Connect(function()
        respawnPlayerInLobby(player)
        removeActivePlayer(player)
    end)
end

local function onPlayerJoin(player)
    player.RespawnLocation = lobbySpawn
end

local function removePlayerWeapon(whichPlayer)
    -- Check to see if a player exist in case they disconnected or left.
    if whichPlayer then
        local character = whichPlayer.Character

        -- If the player has it currently on their character
        if character:FindFirstChild("Weapon") then
            character.Weapon:Destroy()
        end
        -- If the player has the weapon in their backpack
        if whichPlayer.Backpack:FindFirstChild("Weapon") then
            whichPlayer.Backpack.Weapon:Destroy()
        end
    else
        print("No player to remove weapon")
    end
end

function PlayerManager.sendPlayersToMatch()
    local availableSpawnPoints = spawnLocations:GetChildren()

    for playerKey, whichPlayer in pairs(Players:GetPlayers()) do
        table.insert(activePlayers,whichPlayer)

        -- Gets a spawn location and then removes it from the table so the next player gets the next spawn
        local spawnLocation = availableSpawnPoints[1]
        table.remove(availableSpawnPoints, 1)
        preparePlayer(whichPlayer, spawnLocation)
    end

    playersLeft.Value = #activePlayers
end

function PlayerManager.getWinnerName()
    if activePlayers[1] then
        local winningPlayer = activePlayers[1]
        return winningPlayer.Name
    else
        return "Error: No player found"
    end
end

function PlayerManager.removeAllWeapons()
    for playerKey, whichPlayer in pairs(activePlayers) do
        removePlayerWeapon(whichPlayer)
    end
end

function PlayerManager.resetPlayers()
    for playerKey, whichPlayer in pairs(activePlayers) do
        respawnPlayerInLobby(whichPlayer)
    end

    activePlayers = {}
end

-- Events
Players.PlayerAdded:Connect(onPlayerJoin)

return PlayerManager

コメントを残す