4.タイマーとイベント

ラウンドの間、プログラムはタイムを追跡し、異なるスクリプトの間で信号を送る必要があります。時間はタイムスクリプトで管理し、Robloxのコーディングの概念であるイベントは、試合終了などの変化を知らせます。

イベントでシグナルを送信する

プレイヤーがアリーナにいる状態で、イベントを使って試合開始を合図し、タイマーのコードを開始することができます。その後、イベントを使って試合終了を知らせ、プレイヤーをロビーに戻すことができます。

これらのイベントはあらかじめ用意されているものではないので、バインダブル(bindable) イベントと呼ばれるカスタムイベントのオブジェクトを作成する必要があります。バインダブルイベントは、プレイヤーからのアクションによく使われ、Touched や Changed のようなイベントに似ています。

複数のスクリプトが同じバインダブルイベントをリッスンすることができます。これにより、コードが整理され、必要に応じて試合の開始や終了のためのコードを後から追加することが容易になります。

バインド可能なイベントの作成

試合の開始時と終了時にバインド可能なイベント オブジェクトを作成することから始めます。バインド可能なイベントはクライアントと対話しないため、ServerStorage に格納できます。

  1. ServerStorage で、Events という名前の新しいフォルダーを作成します。そのフォルダーに MatchStart とMatchEndという名前の 2 つの BindableEvents を作成します。

イベントの使用

現在、プレイヤーがアリーナに入場すると、タイマーを開始するのではなく、インターミッションが再開され続けています。メインループは、次のコードに移る前に、MatchEndイベントが発生するまで停止して待機するように指示する必要があります。

イベントには2つの組み込み関数があります。Connect() と Wait() です。以前のように Connect() を使う代わりに、MatchEnd で Wait() を呼び、MatchEnd が発生するまでゲームマネージャースクリプトを一時停止させます。この場合、ゲームマネージャーが試合終了のシグナルを受け取るまで、wait 関数がコードを一時停止させます。

  1. GameManagerで、Event フォルダと MatchEnd イベント用の変数を作成します。
-- Module Scripts
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local matchManager = require(moduleScripts:WaitForChild("MatchManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))

-- Events
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")
  1. スクリプトは、試合の終了イベントが発生するのを待ってから次に進むようにします。ループの最後に、次のように入力します。
    matchEnd.Event:Wait()
while true do
    repeat
        wait(gameSettings.intermissionDuration)
        print("Restarting intermission")
    until Players.NumPlayers >= gameSettings.minimumPlayers

    print("Intermission over")
    wait(gameSettings.transitionTime)

    matchManager.prepareGame()
    -- Placeholder wait for the length of the game.
    matchEnd.Event:Wait()
end
  1. ゲームをテストしてください。プレイヤーがアリーナに入った後、中断ループが続かないことを確認します。スクリプトは、matchEnd シグナルの発行を待っている状態です。

トラブルシューティング

この時点でコードが期待通りに動作しない場合は以下のいずれかを試してみてください。

  • matchEnd.Event:Wait() でのドットまたはコロン演算子の使用を再確認してください。
  • MatchEnd が BindableEvent であり、RemoteEvent などの別のタイプではないことを確認してください。

タイマーの使用

試合終了の条件のひとつにタイムアップがありますが、これはスクリプトで処理されます。

タイマーの設定

ゲームにタイマーを追加するには、以下の手順で作成したモジュールスクリプトを使用します。タイマーの開始と終了、残り時間を返す関数が含まれています。

  1. ServerStorage > ModuleScripts で、Timer という名前の新しいモジュール スクリプトを作成します。

以下のコードに置き換えます。

local Timer = {}
Timer.__index = Timer

function Timer.new()
    local self = setmetatable({}, Timer)

    self._finishedEvent = Instance.new("BindableEvent")
    self.finished = self._finishedEvent.Event

    self._running = false
    self._startTime = nil
    self._duration = nil

    return self
end

function Timer:start(duration)
    if not self._running then
        local timerThread = coroutine.wrap(function()
            self._running = true
            self._duration = duration
            self._startTime = tick()
            while self._running and tick() - self._startTime < duration do
                wait()
            end
            local completed = self._running
            self._running = false
            self._startTime = nil
            self._duration = nil
            self._finishedEvent:Fire(completed)
        end)
        timerThread()
    else
        warn("Warning: timer could not start again as it is already running.")
    end
end

function Timer:getTimeLeft()
    if self._running then
        local now = tick()
        local timeLeft = self._startTime + self._duration - now
        if timeLeft < 0 then
            timeLeft = 0
        end
        return timeLeft
    else
        warn("Warning: could not get remaining time, timer is not running.")
    end
end

function Timer:isRunning()
    return self._running
end

function Timer:stop()
    self._running = false
end

return Timer
  1. MatchManagerでは、GameSettings モジュールと Timer モジュールを必要とします。
local MatchManager = {}

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

-- 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"))
  1. timer.new() で新しいタイマーオブジェクトを作成し、myTimer 変数に代入しておきます。このオブジェクトを使ってタイマーの開始と停止を制御します。
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local timer = require(moduleScripts:WaitForChild("Timer"))

-- Creates a new timer object to be used to keep track of match time.
local myTimer = timer.new()
タイマーモジュールのスクリプトは他のスクリプトから呼び出すことができ、特定の時間後にトラップを出現させたい場合など、必要に応じてさらにタイマーを作成することができます。複数の状況でタイマーを使用するには、myTrapTimer = timer.new() のように、新しいタイマー・オブジェクトを作成します。タイマー・オブジェクトは、再利用するのではなく、一つの目的にのみ使用すべきです。

起動と停止

タイマーを作成したので、次にそのオブジェクト内の start() と stop() 関数を使用します。以下は、その関数の解説です。

  • start(time)  : 時間(秒)をパラメーターとしてタイマーを開始します。
  • finished:Connect(functionName) : タイマーが終了すると、パラメーターで渡された関数を実行します。
  1. MatchManager で、 timeUp() という名前の新しい関数を作成して、タイマーが終了するたびに実行するようにします。テストのために print 文を入れておきます。
local myTimer = timer.new()

-- Local Functions
local function timeUp()
    print("Time is up!")
end

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

return MatchManager
  1. timeup() の下に、print 文を含む startTimer という名前の関数を追加します。タイマーは後でゲーム内に表示します。
-- Local Functions
local function timeUp()
    print("Time is up!")
end

local function startTimer()
    print("Timer started")
end
  1. タイマーの開始と停止は startTimer() で行います。
  • myTimer.start() を呼び出します。gameSettings.matchDurationを渡します。
  • myTimer.finished:Connect() を呼び出します。timeUp 関数を渡します。
-- Local Functions
local function startTimer()
    print("Timer started")
    myTimer:start(gameSettings.matchDuration)
    myTimer.finished:Connect(timeUp)
end

タイマーを開始する

MatchStart イベントを使って、試合開始時にタイマーをスタートさせることができます。

  1. MatchManager のモジュール変数の下に、Event フォルダ、MatchStart、MatchEnd(これは今後のレッスンで使用します)を格納するための変数を作成します。
-- 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")

--Creates timer
local myTimer = timer.new()
  1. 「return MatchManager」の上で、試合開始イベントをstartTimer 関数に接続します。
-- Module Functions
function MatchManager.prepareGame()
    playerManager.sendPlayersToMatch()
end

matchStart.Event:Connect(startTimer)

return MatchManager
  1. 試合開始イベントを発生させるには、prepareGame() の中で matchStart:Fire() と入力します。
-- Module Functions
function MatchManager.prepareGame()
    playerManager.sendPlayersToMatch()
    matchStart:Fire()
end
  1. ゲームをテストします。出力ウィンドウで、タイマーのスタートとストップ関数のprint文が表示されていることを確認します。

完成したスクリプト

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

MatchManager スクリプト

local MatchManager = {}

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

-- 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")

-- Creates a new timer object to be used to keep track of match time.
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)
end

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

matchStart.Event:Connect(startTimer)

return MatchManager

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"))

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

while true do
    repeat
        wait(gameSettings.intermissionDuration)
        print("Restarting intermission")
    until Players.NumPlayers >= gameSettings.minimumPlayers

    print("Intermission over")
    wait(gameSettings.transitionTime)

    matchManager.prepareGame()
    -- Placeholder wait for the length of the game.
    matchEnd.Event:Wait()
end

コメントを残す