6-2 モジュールスクリプトを使用した制作

ここでは、プレイヤーがロックピックを拾い、それを使って宝箱を開けることができるというモジュールスクリプトを作成します。

プロジェクトのセットアップ

学習用に用意したプロジェクトがファイルがありますので、それをダウンロードして使用します。このファイルには、リーダーボードを表示するプログラムと、ロックピックと宝箱用のモデルと未完成のスクリプトが含まれます。

プロジェクトを読み込む

  1. プロジェクトをダウンロードします。こちらはRoblox Studio でそのまま開けるファイルです。
  1. ダウンロードしたファイルをRoblox Studioで開きます。

モジュールスクリプトを作成する

プレイヤーがチェストから宝物を取得できるように、TreasureManagerという名前のモジュールスクリプトを作成します。ロックピックを拾った時にこの中のモジュール関数を呼び、リーダーボードの更新ができるようにします。

  1. ServerStorageで、新しい ModuleScript を作成し、名前を TreasureManager に変更します。
  1. TreasureManager で、2か所にある module を TreasureManager に変更します。
local TreasureManager = {}

return TreasureManager

モジュールスクリプトでの関数の使用

getLockpick() という名前の新しいモジュール関数を作成します。この関数は、指定のロックピックを消去し、プレーヤーのインベントリ内にある lockpicks の数に1を加算するという処理を行います。

Lockpicks のモジュール関数を作成する

  1. このモジュール スクリプトは、モジュールとローカル関数の組み合わせを使用します。2 つのコメントを入力して、それらを分けるのに役立てます。
local TreasureManager = {}

------------------ Local Functions

------------------ Module Functions

return TreasureManager
  1. コメント Module Functions の下に getLockpick() という名前の新しいモジュール関数を追加します。
    この関数は次の 2 つのパラメーターを使用します。
  • lockpickPart ー 消去するロックピックパーツ
  • whichCharacter ー ロックピックに触れたプレイヤー
local TreasureManager = {}

------------------ Local Functions

------------------ Module Functions
function TreasureManager.getLockpick(lockpickPart, whichCharacter)

end

return TreasureManager
  1. getLockpick()で、lockpickPart を消去します。
function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    lockpickPart:Destroy()
end

モジュール機能を使う

これでモジュール関数 getLockpick() が他のスクリプトから使用できるようになりました。その関数をテストするには、事前に作成されたスクリプトを開いて呼び出します。

  1. Workspace の Lockpicks フォルダに含まれる LockpickScript を開きます。
  2. require() を使用し、モジュールスクリプトを tresureManager 変数に入れます。
local ServerStorage = game:GetService("ServerStorage")
-- Require the module script below ⯆
local treasureManager = require(ServerStorage:WaitForChild("TreasureManager"))

local lockpicks = script.Parent
local lockpicksFolder = lockpicks.Parts
local lockpicksArray = lockpicksFolder:GetChildren()
なぜ WaitForChild()?

require(ServerStorage:WaitForChild("TreasureManager")) は、require(ServerStorage.TreasureManager) と記述することもできます。ではなぜ、WaitForChild() 関数を使用するのでしょうか。この関数は、FindFirstChild() と同等の機能があるものですが、名前の通り、指定したオブジェクトが見つかるまで少しの間待機するという機能が備わっているものです。ゲームが起動して、すぐにこの LockpickScript が実行された場合、タイミングによっては、ServerStorage 内に TreasureManager が存在しないことがあります。この関数を使うことで、そのようなエラーを未然に防ぐことができるのです。
なお、ServerScriptService または ServerStorage のスクリプトの場合、ServerStorage.TreasureManagerのように、ドット演算子を使用しても大丈夫です。
  1. プレイヤーがパーツに触れているかどうかをチェックする partTouched() という名前の関数が既にあります。
    そこに先ほど作成したモジュール関数を呼ぶコードを追加しましょう。
  • getLockpick() モジュール関数を呼び出して、 lockpick を破棄します。
  • lockpickPartとwhichCharacter を渡します。
local ServerStorage = game:GetService("ServerStorage")
-- Require the module script below ⯆
local treasureManager = require(ServerStorage:WaitForChild("TreasureManager"))

local lockpicks = script.Parent
local lockpicksFolder = lockpicks.Parts
local lockpicksArray = lockpicksFolder:GetChildren()

local function partTouched(otherPart, lockpickPart)
    local whichCharacter = otherPart.Parent
    local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
        -- Give the player a lockpick and destroy the lockpick part
        -- =============================================
        treasureManager.getLockpick(lockpickPart, whichCharacter)
        -- =============================================
    end
end
  1. プロジェクトを実行し、ロックピックに触れるとそれが消えることを確認します。

トラブルシューティング

「Infinite yield possible」などのエラー メッセージが表示される
  • スクリプト内のモジュールスクリプトのスペルを確認します。TreasureManager などのモジュール スクリプトのスペルが異なる場合エラーが発生します。
「attempt to index global」というエラー メッセージが表示される
  • LockpickScript のモジュール スクリプトの require を含む行を確認します。モジュールに require が含まれていない場合、そのモジュールスクリプトの関数と変数を使用できません。
スクリプトが実行されないか、ロックピックを取得できない
  • モジュールスクリプトで、すべてのコードが「local TreasureManager = {}」と「return TreasureManager」の間にあることを確認します。return は、モジュール スクリプトの最後の行である必要があります。
  • 「WaitForChild(“TreasureManager”))」のように、require の行の最後に 2 つの括弧があることを確認します。

ローカル関数を作成する

プレーヤーのロックピックとトレジャー リーダーボードの値を変更するには、モジュール スクリプトでローカル関数を使用します。プレーヤーの変数を変更する関数は、このモジュールスクリプト内でのみ使用されるため、ローカル関数です。

  1. ServerStorage の TreasureManager を開きます。
  2. 次のことを行うローカル変数を作成します。
  • Players サービスを取得して、スクリプトがプレーヤーのリーダーボード統計と連携できるようにします。
  • LockpickPartに触れた後にプレーヤーが受け取るロックピックの数を保存します。
local TreasureManager = {}

local Players = game:GetService("Players")
local lockpickDrop = 1

------------------ Local Functions

------------------ Module Functions
function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    lockpickPart:Destroy()
end

return TreasureManager
  1. これら 2 つのローカル関数をコピーして、「Local Functions」コメントの下に貼り付けます。
  • getPlayerLockpicks() は、プレイヤーの leaderstats 内の Lockpicks を返します。
  • getPlayerTreasure()は、プレイヤーの leaderstats 内の Treasure を返します。
------------------ Local Functions
local function getPlayerLockpicks(whichCharacter)
    local player = Players:GetPlayerFromCharacter(whichCharacter)
    local leaderstats = player:FindFirstChild("leaderstats")
    return leaderstats:WaitForChild("Lockpicks")
end

local function getPlayerTreasure(whichCharacter)
    local player = Players:GetPlayerFromCharacter(whichCharacter)
    local leaderstats = player:FindFirstChild("leaderstats")
    return leaderstats:WaitForChild("Treasure")
end

------------------ Module Functions
  1. プレーヤーのロックピックに追加するには、getLockpick() モジュール関数で次の処理を行います。
  • getPlayerLockpicks(whichCharacter) を呼び出し、その戻り値をローカル変数に入れる。
  • lockpickDrop の値を playerLockpicks に追加します。

getPlayerLockpicks() の戻り値は、IntValue になります。よって、そのプロパティ Value に数値が入ります。

------------------ Module Functions
function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    playerLockpicks.Value = playerLockpicks.Value + lockpickDrop
    lockpickPart:Destroy()
end
  1. プロジェクトを実行します。ロックピックに触れると消去され、リーダーボードのプレイヤーの Lockpicks に 1 が加算されることを確認します。
local TreasureManager = {}

local Players = game:GetService("Players")
local lockpickDrop = 1

------------------ Local Functions
local function getPlayerLockpicks(whichCharacter)
    local player = Players:GetPlayerFromCharacter(whichCharacter)
    local leaderstats = player:FindFirstChild("leaderstats")
    return leaderstats:WaitForChild("Lockpicks")
end

local function getPlayerTreasure(whichCharacter)
    local player = Players:GetPlayerFromCharacter(whichCharacter)
    local leaderstats = player:FindFirstChild("leaderstats")
    return leaderstats:WaitForChild("Treasure")
end

------------------ Module Functions
function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    playerLockpicks.Value = playerLockpicks.Value + lockpickDrop
    lockpickPart:Destroy()
end

return TreasureManager

トラブルシューティング

  • すべてのローカル関数がモジュール関数の上にあることを確認してください。モジュール関数は、その下にあるローカル関数を使用できません。

モジュールスクリプトからの情報の取得

プレイヤーが宝箱を開ける前に、少なくても1つ以上のロックピックを持っているかどうかを確認しなくてはなりません。その際に必要となるモジュール関数を作成します。

チェストが開けられるかどうかを確認する

  1. ServerStorage の TreasureManager で、チェストを開けるのに必要なロックピックの数と、各チェストに入っているゴールドの量の変数を設定します。
local TreasureManager = {}

local Players = game:GetService("Players")
local lockpickDrop = 1
local chestPickCost = 1
local chestReward = 100

------------------ Local Functions
local function getPlayerLockpicks(whichCharacter)
    local player = Players:GetPlayerFromCharacter(whichCharacter)
    local leaderstats = player:FindFirstChild("leaderstats")
    return leaderstats:WaitForChild("Lockpicks")
end
  1. プレーヤーがチェストを開けることができるかどうかを確認する関数を作ります。コメント「Module Functions」の下に、canOpenChest() という関数を作成します。パラメーターはプレイヤーになります。
------------------ Module Functions
function TreasureManager.canOpenChest(whichCharacter)
end

function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    playerLockpicks.Value = playerLockpicks.Value + lockpickDrop
    lockpickPart:Destroy()
end
  1. 以下のコードをコピーして canOpenChest() に貼り付け、プレーヤーがロックピックを持っている場合は true を返し、まだ持っていない場合は false を返します。
function TreasureManager.canOpenChest(whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    if playerLockpicks.Value >= 1 then
        return true
    else
        return false
    end
end

プレイヤーに宝物を与える

プレイヤーが宝箱を開けたときに実行される関数を作成します。プレイヤーに宝物を与えます。

  1. openChest() という名前の新しいモジュール関数を TreasureManager に追加します。
    この関数には2つのパラメーターがあります。
  • chestPart ー  宝箱チェスト
  • whichCharacter ー 宝物を与えるプレイヤー
function TreasureManager.openChest(chestPart, whichCharacter)

end
  1. 以下のコードをコピーして openChest() に貼り付けてください。
    プレイヤーのロックピック数を減らし、ゴールドを与えます。最後にチェストを消します。
function TreasureManager.openChest(chestPart, whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    local playerTreasure = getPlayerTreasure(whichCharacter)
    playerLockpicks.Value = playerLockpicks.Value - chestPickCost
    playerTreasure.Value = playerTreasure.Value + chestReward
    chestPart:Destroy()
end

チェスト関係のモジュール関数を呼び出す

2つのモジュール関数 canOpenChest() と openChest() が完成したところで、プレイヤーがチェストに触れたときに、この関数を呼び出すようにします。

  1. Workspace の中の Chests フォルダにある ChestScript を開きます。
  2. LockpickScript で行ったように、require() を使ってモジュールスクリプトをロードし、treasureManager 変数に代入しておきます。
local ServerStorage = game:GetService("ServerStorage")
-- Require the module script below
local treasureManager = require(ServerStorage:WaitForChild("TreasureManager"))

local chests = script.Parent
local chestsFolder = chests.Parts
local chestsArray = chestsFolder:GetChildren()
  1. partTouched() の「 if humanoid」ステートメントの下に、先ほど作成したモジュール関数 canOpenChest() を呼び出し、その値を canOpen 変数に代入するようにします。この変数には true か false が入ります。

TreasureManager.canOpenChest(どのキャラクター)

local function partTouched(otherPart, chestPart)
    local whichCharacter = otherPart.Parent
    local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
        -- Check if the player can open a chest, then let them get treasure
        -- =============================================
        local canOpen = treasureManager.canOpenChest(whichCharacter)
        -- =============================================
    end
end
  1. 次に canOpen が true かどうかを確認する if ステートメントを追加します。true だった場合は、ピックロックを持っていますので宝箱を開るために、openChest() を呼びます。
local function partTouched(otherPart, chestPart)
    local whichCharacter = otherPart.Parent
    local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
        -- Check if the player can open a chest, then let them get treasure
        -- =============================================
        local canOpen = treasureManager.canOpenChest(whichCharacter)

        if canOpen == true then
            treasureManager.openChest(chestPart, whichCharacter)
        end
        -- =============================================
    end
end
  1. プロジェクトを実行し、以下の動作を確認します。
  • ロックピックが 1つ以上ある場合、チェストに触れるとチェストが消え、宝物が手に入ります。
  • ロックピックが0の場合、宝箱を開けられません。

トラブルシューティング

  • モジュール関数を呼ぶ側と、呼ばれる側の関数のスペルは完全に一致していないとエラーになります。
  • すでに入っているプログラムが常に正しいとも限りません。単語の下に赤いアンダーバーがある部分は修正が必要なところです。エラーになっている箇所は修正してください。

完成したプロジェクトのサンプル

プロジェクトファイル

完成したプロジェクトをここからダウンロードして確認できます。

完成したスクリプト

完成した TreasureManager
local TreasureManager = {}

local Players = game:GetService("Players")
local lockpickDrop = 1
local chestPickCost = 1
local chestReward = 100

------------------ Local Functions
local function getPlayerLockpicks(whichCharacter)
    local player = Players:GetPlayerFromCharacter(whichCharacter)
    local leaderstats = player:FindFirstChild("leaderstats")
    return leaderstats:WaitForChild("Lockpicks")
end

local function getPlayerTreasure(whichCharacter)
    local player = Players:GetPlayerFromCharacter(whichCharacter)
    local leaderstats = player:FindFirstChild("leaderstats")
    return leaderstats:WaitForChild("Treasure")
end

------------------ Module Functions
function TreasureManager.canOpenChest(whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    if playerLockpicks.Value >= 1 then
        return true
    else
        return false
    end
end

function TreasureManager.openChest(chestPart, whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    local playerTreasure = getPlayerTreasure(whichCharacter)

    playerLockpicks.Value = playerLockpicks.Value - chestPickCost
    playerTreasure.Value = playerTreasure.Value + chestReward
    chestPart:Destroy()
end

function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    playerLockpicks.Value = playerLockpicks.Value + lockpickDrop
    lockpickPart:Destroy()
end

return TreasureManager
完成した ChestScript
local ServerStorage = game:GetService("ServerStorage")
-- Require the module script below ⯆
local treasureManager = require(ServerStorage:WaitForChild("TreasureManager"))

local chests = script.Parent
local chestsFolder = chests.Parts
local chestsArray = chestsFolder:GetChildren()

local function partTouched(otherPart, chestPart)
    local whichCharacter = otherPart.Parent
    local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
        -- プレイヤーが宝箱を開けられるかどうかを確認し、宝物を取得させる
        -- =============================================
        local canOpen = treasureManager.canOpenChest(whichCharacter)
        if canOpen == true then
            treasureManager.openChest(chestPart, whichCharacter)
        end
        -- =============================================
    end
end

-- すべてのロックピックのパーツをタッチイベントに接続し機能するようにする
for chestIndex = 1, #chestsArray do
    local chestPart = chestsArray[chestIndex]
    chestPart.Touched:Connect(function(otherPart)
        partTouched(otherPart, chestPart)
    end)
end
完成した LockpickScript
local ServerStorage = game:GetService("ServerStorage")
-- Require the module script below ⯆
local treasureManager = require(ServerStorage:WaitForChild("TreasureManager"))

local lockpicks = script.Parent
local lockpicksFolder = lockpicks.Parts
local lockpicksArray = lockpicksFolder:GetChildren()

local function partTouched(otherPart, lockpickPart)
    local whichCharacter = otherPart.Parent
    local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
        -- プレイヤーにロックピックを与える
        -- =============================================
        treasureManager.getLockpick(lockpickPart, whichCharacter)
        -- =============================================
    end
end

-- すべてのロックピックのパーツをタッチイベントに接続し機能するようにする
for lockpickIndex = 1, #lockpicksArray do
    local lockpickPart = lockpicksArray[lockpickIndex]
    lockpickPart.Touched:Connect(function(otherPart)
        partTouched(otherPart, lockpickPart)
    end)
end

コメントを残す