ゲームの最後のフェーズであるクリーンアップとリセットをコーディングします。このフェーズのコードは、ゲームがインターミッションまでループし、次の試合が各プレイヤーにとって同じように開始されるようにします。
GUI の更新
クリーンアップとリセットを行う前に、DisplayManager を使用してゲームがどのように終了したかをプレーヤーに通知し、適切なメッセージを表示します。
勝者の名前を取得する
勝者がいる場合は、勝者の名前を取得することから始めます。以前のコードでは、activePlayers テーブルのサイズが 1 になったかどうかをチェックしていました。残りのプレイヤーの名前を取得するには、そのテーブルの最初のインデックスにある名前を返します。
- PlayerManager で、 getWinnerName() という名前の新しいモジュール関数を作成します。
function PlayerManager.getWinnerName()
end
-- Events
Players.PlayerAdded:Connect(onPlayerJoin)
- activePlayers[1] に何かが存在する場合に実行する if 文を追加します。以前はテーブル数をチェックしていましたが、プレイヤーのネットが切断されたり、ゲームから退出した可能性があります。
function PlayerManager.getWinnerName()
if activePlayers[1] then
end
end
- 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で表示します。
- 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
- 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
- 各条件について以下を追加してください。
【勝者がいた場合】
- 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
- 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 でプレイヤーに表示します。
- 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
- 返されたメッセージを GUI で表示するには、displayManager.updateStatus() を呼び出して endStatus を渡します。
local endStatus = matchManager.getEndStatus(endState)
displayManager.updateStatus(endStatus)
- プレイヤーにメッセージを見せるために、transitionTime を使用して待ち時間を追加します。
local endStatus = matchManager.getEndStatus(endState)
displayManager.updateStatus(endStatus)
wait(gameSettings.transitionTime)
- テストサーバーを起動し、タイムアップと勝利プレイヤーの2つの条件で、以下のメッセージがプレイヤーに表示されることを確認してください。
トラブルシューティング
このようにメッセージが表示されない場合は、以下のいずれかを試してください。
- 終了メッセージが「Error Found」の場合、どの条件も成功しなかったことになります。MatchManager.getEndStatus() 内のコードとこのページの最後に掲載されているサンプルコードを照らし合わせてみてください。
- 終了メッセージが表示されない場合は、wait(gameSettings.transitionTime) が displayManager を呼び出した後にあることを確認します。
新しい試合の開始
新しい試合を開始する前に、短い移行処理を行います。これにより、プレイヤーは終了メッセージを見る時間ができ、いきなりロビーに転送されることがなくなります。
移行が終了すると、残っているプレイヤーはアリーナから排除され、すべてのコードがリセットされます。これにより、プレイヤーはゲームがクリーンな状態で次の試合を開始することができます。
移行への対応
プレイヤーが移行状態に入ったら、武器を取り外します。
- 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
- removeAllWeapons() という名前の新しいモジュール関数を作成します。
function PlayerManager.removeAllWeapons()
end
-- Events
Players.PlayerAdded:Connect(onPlayerJoin)
return PlayerManager
- その関数の中で、pairs() を使ったforループを使い、activePlayers に含まれるアクティブプレイヤーを調べていきます。そのループの中でプレイヤーを引数とし、removePlayerWeapon() を呼び出します。
function PlayerManager.removeAllWeapons()
for playerKey, whichPlayer in pairs(activePlayers) do
removePlayerWeapon(whichPlayer)
end
end
試合間の掃除
クリーンアップは、MatchManager の独自の機能になります。今のところ、クリーンアップは以前に作成された関数を使用してプレイヤーの武器を削除します。ゲームを拡張すると、試合中に変更されたマップをリセットする機能など、さらに追加できます。
- MatchManager を開きます。cleanupMatch() という名前の新しいモジュール関数を追加します。その関数で、playerManager.removeAllWeapons() を呼び出します。
function MatchManager.cleanupMatch()
playerManager.removeAllWeapons()
end
matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)
return MatchManager
- 次に、クリーンアップ関数を呼び出します。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
- テストサーバーを起動し、試合を実行します。タイマーが切れるのを待ち、休憩タイムに入った際にプレーヤーの武器が取り外されていることを確認します。
試合のリセット
試合終了後もアリーナにプレイヤーが残っているなど、いくつか気づいたことがあるかもしれません。試合がクリーンアップされたら、次にゲームをリセットします。これは、アリーナにいるプレイヤーをロビーに戻すことと、アクティブプレイヤーテーブルをクリアすることを含みます。リセットを行うことで、ゲームループを無限に走らせることができます。
まず、プレイヤーをロビーに戻す関数を開始します。
- 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
- activePlayersテーブルに {} を代入することで空になります。空の配列(テーブル)にリセットするもっとも早い方法です。
function PlayerManager.resetPlayers()
for playerKey, whichPlayer in pairs(activePlayers) do
respawnPlayerInLobby(whichPlayer)
end
activePlayers = {}
end
- MatchManager を開きます。resetMatch() という名前の新しいモジュール関数を作成し playerManager.resetPlayers() を呼び出します。
function MatchManager.resetMatch()
playerManager.resetPlayers()
end
matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)
return MatchManager
- 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
- テスト サーバーを起動し、試合を実行します。少なくとも 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
[ 6.試合終了 ]
[ 8.プロジェクトの終了 ]