現在、ゲーム情報の多くは、プレイヤーからは見えない「出力ウィンドウ」に表示されています。そこで、プレイヤーにゲームの状況を知らせるために、グラフィカル・ユーザー・インターフェース(GUI)を作成し、それをコーディングすることになります。
GUI による情報の表示
このゲームでは、テキストラベルに現在のゲーム ステータスと、残りのプレイヤー数と時間が表示されます。
GUI のセットアップ
まず、さまざまなテキスト要素を保持するために、ScreenGUIオブジェクトを作成します。プレイヤーがカメラを動かしても、ScreenGUIはいつもスクリーン上の同じ場所に表示されます。
すべてのプレイヤーが同じ画面を見ることができるように、GUIはStarterGUIフォルダに配置します。ゲーム起動時に、このフォルダが全プレイヤーにコピーされます。
- StarterGUI フォルダに、新しい ScreenGUI を作成します。そして ScreenGUIに、StatusTextという名前の新しいTextLabelを追加します。
- テキストラベルを移動するには、エクスプローラで「StatusText」を選択します。次に、ゲームビューで、ラベルを好きな場所にドラッグします。数値は動画と異なる場合があります。ラベルのサイズは、周囲に表示されているアンカーポイントをドラッグして変更することもできます。
GUI のスクリプト作成
ゲームの変更を反映するには、スクリプトで GUI 要素を更新する必要があります。たとえば、ゲームのステータスは、休憩中か試合中かに関係なく、StringValue に保存され、ローカル スクリプトを使用して更新されます。
Robloxサーバーを動かすスクリプトやモジュールスクリプトに比べ、ローカルスクリプトはプレイヤーのデバイス上で動きます。ローカルスクリプトは、タイマーのようなGUI要素や、マウスやキーボードの操作のようなプレイヤー入力のコーディングによく使われます。
スクリプトの設定
StatusDisplay スクリプトは、ゲームの状態が変わるたびにプレイヤーの GUI を更新するために使用されます。
- ReplicatedStorageで、DisplayValues という名前のフォルダを作成します。そのフォルダに、Status という名前の StringValue を追加します。後で値をテストするには、「Welcome to the Battle!」などの一時的な値を入力します。
ローカルスクリプトはプレイヤーの端末でのみ実行されるため、ServerStorageのようなServerフォルダに保存することはできません。ReplicatedStorageは、クライアント(デバイス)とサーバーの両方が利用できるフォルダです。
- StarterGUI > ScreenGUI > StatusText に、StatusDisplay という名前の新しい LocalScript を追加します。GUI に影響を与えるスクリプトは、対象となる GUI オブジェクトの下に作成するのが一般的です。
- StatusDisplay を開き、以下の変数を定義します。
ReplicatedStorage、DisplayValues フォルダ、Status、TextLabel
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local status = displayValues:WaitForChild("Status")
local textLabel = script.Parent
テキストラベルの変更
ラベルのテキストを変更するには、Changed イベントを使用して、Status 文字列が別のスクリプトによって変更されるたびにテキストラベルが更新されるようにします。
- updateText() という名前の新しい関数をコーディングします。その関数で、textLabel の Text プロパティをstatus.Value に設定します。
local textLabel = script.Parent
local function updateText()
textLabel.Text = status.Value
end
- 関数を Changed イベントに接続します。
local function updateText()
textLabel.Text = status.Value
end
status.Changed:Connect(updateText)
- ゲーム開始時にプレイヤーに最新の状態を見せるために、スクリプトの最後で updateText() を実行します。
local function updateText()
textLabel.Text = status.Value
end
status.Changed:Connect(updateText)
updateText()
- ゲームを実行し、ディスプレイに一時的な値が表示されることを確認します。
表示マネージャの作成
ゲーム中、テキストラベルは GameManager、MatchManager、そして場合によっては他のスクリプトから情報を得る必要があります。そこで、DisplayManager という名前のモジュールスクリプトを作成し、これらのスクリプトが必要なときにテキストラベルを更新できるようにします。
スクリプトの設定
DisplayManager は他のスクリプトとやり取りする必要があるため、モジュールスクリプトになります。
- ServerStorage の ModuleScripts の中に、DisplayManager という名前の新しいモジュールスクリプトを作成します。スクリプト名と一致するようにモジュールテーブルの名前を変更します。
- 次のローカル変数を追加します。
ReplicatedStorage、DisplayValues フォルダ、Status
local DisplayManager = {}
-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Display Values used to update Player GUI
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local status = displayValues:WaitForChild("Status")
-- Local Functions
-- Module Functions
return DisplayManager
- Status 値の文字列を更新する updateStatus() という名前の新しいモジュール関数を作成します。他のスクリプトは、この関数を呼び出すことができます。
-- Local Functions
-- Module Functions
function DisplayManager.updateStatus(newStatus)
status.Value = newStatus
end
テキストステータスの更新
Display Manager を設定すると、他のスクリプトで使用して GUI テキスト ラベルを更新できます。GUI の最初のメッセージとして、GameManager スクリプトを介して休憩の開始と終了を表示します。
- ServerScriptService の GameManager で、displayManagerという名前の変数を作成し、ServerStorage で DisplayManager モジュールを要求します。
-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
-- Module Scripts
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local roundManager = require(moduleScripts:WaitForChild("RoundManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local displayManager = require(moduleScripts:WaitForChild("DisplayManager"))
- while true do 内の最初の行で、displayManager の updateStatus() を呼び出して、プレイヤーに現在待機中であることを伝えるメッセージを表示させます。
-- Events
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")
while true do
displayManager.updateStatus("Waiting for Players")
repeat
print("Starting intermission")
wait(gameSettings.intermissionDuration)
until Players.NumPlayers >= gameSettings.minimumPlayers
wait(gameSettings.transitionTime)
matchManager.prepareGame()
matchEnd.Event:Wait()
end
- 待機中のループから抜け出た後、updateStatus() を呼び出し、試合が開始されたことを伝えるメッセージを表示します。GUI でテストするので、休憩の開始と終了を示す 2 つの print 文は削除します。
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()
matchEnd.Event:Wait()
end
- 最小限のプレーヤーがいる場合といない場合で、ゲームをテストしてください。メッセージは以下のように表示されるはずです。
- 最小プレイヤーなしで: “Waiting for Players”
- 最小限のプレーヤーで:”Get ready”
トラブルシューティング
この時点で、テキストラベルに最初のメッセージが表示されない場合、または「ラベル」が引き続き表示される場合は、次のいずれかを試してください。
- StatusDisplay のローカル スクリプトで、updateText() がスクリプトの一番下で呼び出されていることを確認します。これにより、プレーヤーは最新のメッセージを受け取ることができます。
- StringValue の Status が ReplicatedStorage にあることを確認します。クライアントとサーバーの関係は独特なので、ServerStorage にあると、ローカルスクリプトが参照できなくなります。
対戦状況の表示
試合中、GUI には残りのプレイヤー数と時間の 2 つの数字が表示されます。これらの数値が変化すると、テキスト ラベルも変化します。
値と機能の設定
IntValues は、プレイヤー数と残り時間を格納するために使用されます。
- ReplicatedStorage の DisplayValues の中に、PlayersLeft と TimeLeft という名前の 2 つの IntValues を作成します。
- DisplayManagerに、PlayerLeft と TimeLeft の値を格納する変数を追加します。
local DisplayManager = {}
-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Display Values used to update Player GUI
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local status = displayValues:WaitForChild("Status")
local playersLeft = displayValues:WaitForChild("PlayersLeft")
local timeLeft = displayValues:WaitForChild("TimeLeft")
- updateMatchStatus() という名前のローカル関数を作成します。次に status の値を設定して、残りのプレイヤー数と残り時間を表示します。
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local status = displayValues:WaitForChild("Status")
local playersLeft = displayValues:WaitForChild("PlayersLeft")
local timeLeft = displayValues:WaitForChild("TimeLeft")
-- Local Functions
local function updateRoundStatus()
status.Value = "Players Left: " .. playersLeft.Value .. " / Time Left: " .. timeLeft.Value
end
- 両方の IntValue 変数に、 updateRoundStatus() を Changed イベントに接続します。
-- Module Functions
function DisplayManager.updateStatus(newStatus)
status.Value = newStatus
end
playersLeft.Changed:Connect(updateRoundStatus)
timeLeft.Changed:Connect(updateRoundStatus)
return DisplayManager
まだテストしないでください。PlayersLeft や TimeLeft の値は何も更新されていないため、試合が開始されてもステータスは変更されません。
プレイヤーの表示
次に、ゲーム開始時にプレイヤー数を表示するコードを追加します。後のレッスンでは、プレーヤーがゲームから除外されると、PlayersLeft の値が更新されます。
- PlayerManager で、ReplicatedStorage サービス、DisplayValues フォルダ、IntValue の PlayersLeft のローカル変数を追加します。
local PlayerManager = {}
-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 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")
- #activePlayers とすることで、配列に追加された人数が分かります。それを playersLeft の値に設定することで、開始プレイヤー数を表示します。
sendPlayersToMatch() の for ループの下に「playersLeft.Value = #activePlayers」と入力します。
function PlayerManager.sendPlayersToMatch()
local availableSpawnPoints = spawnLocations:GetChildren()
for playerKey, whichPlayer in pairs(Players:GetPlayers()) do
table.insert(activePlayers,whichPlayer)
local spawnLocation = availableSpawnPoints[1]
table.remove(availableSpawnPoints, 1)
preparePlayer(whichPlayer, spawnLocation)
end
playersLeft.Value = #activePlayers
end
タイマーを表示する
モジュールスクリプトは、類似のコードを集中管理するために使用されることを忘れないでください。タイマーは MatchManager で追跡されるので、Timer スクリプトの関数を使用して TimeLeft 値を更新します。DisplayManagerは、TimeLeft の変更を受け取り、新しい値に一致するように更新します。
- MatchManager で、 ReplicatedStorageサービス、DisplayValues フォルダ、および TimeLeft 値を格納する変数を作成します。
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 timer = require(moduleScripts:WaitForChild("Timer"))
-- Events
local events = ServerStorage:WaitForChild("Events")
local matchStart = events:WaitForChild("MatchStart")
-- Values
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local timeLeft = displayValues:WaitForChild("TimeLeft")
local myTimer = timer.new()
- startTimer() 関数を探します。タイマーの Finished イベントの後、以下の while ループ全体をコピー&ペーストしてください。このコードでは、タイマーがアクティブである限り、timeLeft の値を更新するループが実行されます。
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
追加すると、コードは次のようになります。
local function startTimer()
print("Timer started")
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
- 最小限のプレイヤーでゲームを実行します。ステータス テキストが表示されることを確認します。
- 正しいスターティングプレーヤーの数です。この数字は、今後のレッスンでコードが追加されるまで変わりませんので覚えておきましょう。
- タイムは1秒ごと減少し、1で停止します。
完成したスクリプト
完成したスクリプトを掲載しますのでチェックしてみてください。
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()
matchEnd.Event:Wait()
end
DisplayManager スクリプト
local DisplayManager = {}
-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Display Values used to update Player GUI
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local status = displayValues:WaitForChild("Status")
local playersLeft = displayValues:WaitForChild("PlayersLeft")
local timeLeft = displayValues:WaitForChild("TimeLeft")
-- Local Functions
local function updateRoundStatus()
status.Value = "Players Left: " .. playersLeft.Value .. " / Time Left: " .. timeLeft.Value
end
-- Module Functions
function DisplayManager.updateStatus(newStatus)
status.Value = newStatus
end
playersLeft.Changed:Connect(updateRoundStatus)
timeLeft.Changed:Connect(updateRoundStatus)
return DisplayManager
MatchManager スクリプト
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 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")
local myTimer = timer.new()
-- Local Functions
local function timeUp()
print("Time is up!")
end
local function startTimer()
print("Timer started")
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
matchStart.Event:Connect(startTimer)
return MatchManager
StatusDisplay スクリプト
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local status = displayValues:WaitForChild("Status")
local textLabel = script.Parent
local function updateText()
textLabel.Text = status.Value
end
status.Changed:Connect(updateText)
updateText()
[ 4.タイマーとイベント ]
[ 6.試合終了 ]