【20】モジュールスクリプトでの作成

モジュールスクリプトの理解を深めるために、プレイヤーがピッキングアイテムを拾い、それを使って宝箱を開けるという簡単なゲームを作成します。

プロジェクトの用意

これからダウンロードするプロジェクトファイルには、リーダーボード付きのスターターマップとピッキングアイテムと宝箱、そして、それらを使用するための初歩的なスクリプトが含まれています。

プロジェクトファイルをロードする

  1. 下記の「ダウンロード」をクリックし、プロジェクトファイルをダウンロードしてください。

ダウンロード

  1. Roblox Studioでダウンロードしたファイルを開きます。
  2. テストプレイしてみましょう。リーダーボードは表示されますが、それ以外はまだ動作しません。

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

プレイヤーがピッキングアイテムを使い、宝箱が開けられるように TreasureManager という名前のモジュールスクリプトを作成します。

  1. ServerStorage に新しい ModuleScript を作成し、名前をTreasureManager に変更します。
  1. 下記のように 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. まずは、拾ったピッキングアイテムは、破壊(消去)します。ずっと存在し続けてしまうと、無限に拾うことができてしまうからです。
function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    lockpickPart:Destroy()
end

モジュール関数を使用する

これにより、モジュール関数 getLockpick() は他のスクリプトから使用できるようになりました。この関数をテストするために、あらかじめ作成してあるスクリプト(LockpickScript)を開きます。

  1. Workspace の Lockpicks フォルダの中にある LockpickScript を開きます。
  2. treasureManager 変数にモジュールスクリプト(TreasureManager)を格納します。

require(ServerStorage:WaitForChild(“TreasureManager”))

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()を使用する理由
ドット演算子の代わりに WaitForChild() を使用して、TreasureManager がロードされるまでスクリプトを待機させてから次の処理に移るようにすることで、エラーを防ぐことができます。
ServerScriptService ServerStorage の中にあるスクリプトでは、ServerStorage.TreasureManager のようにドット演算子を使用する方が安全です。
例:ServerStorage.TreasureManager

  1. LockpickScript には、プレイヤーがピッキングアイテムに触れたときに呼ばれる partTouched() という名前の関数があります。この関数では、まず、触れたのはプレイヤーかどうか、プレイヤーだった場合は、ピッキングアイテムである lockpickPart と 触れたプレイヤーの whichCharacter をモジュール関数 getLockpick() に渡して呼び出します。
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 の間にあることを確認します。これらの行の外にあるコードは他のスクリプトからはアクセスできません。
require が含まれる行の最後の2つの括弧の間にあることを確認します。
例:WaitForChild(“TreasureManager”))

ローカル関数の作成

各プレイヤーが取得したピッキングアイテム数や開けた宝箱などの情報をリーダーボードから取得するには、モジュールスクリプト内のローカル関数を使用します。このモジュールスクリプト内でのみ使用されるので、これらはローカル関数にします。

  1. ServerStorage にある TreasureManager スクリプトを開きます。
  2. 下記の2つのローカル変数を作成します。
  • Players:プレイヤーサービス(容易にリーダーボードにアクセスできるようにするためのもの)
  • lockpickDrop:プレイヤーが ピッキングアイテムに触れたときに取得できるアイテム数
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. Local Functions コメントの下に、下記の2つの関数を入れます。
  • getPlayerLockpicks(): 取得したピッキングアイテム数(Lockpicks)を返します。
  • getPlayerTreasure():取得した宝の値(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) を呼び出し、その戻り値をローカル変数(playerLockpicks)に代入する。
  • lockpickDrop の値を playerLockpicks (取得済みピッキングアイテム数)に加算する。
------------------ Module Functions
function TreasureManager.getLockpick(lockpickPart, whichCharacter)
    local playerLockpicks = getPlayerLockpicks(whichCharacter)
    playerLockpicks.Value = playerLockpicks.Value + lockpickDrop
    lockpickPart:Destroy()
end
  1. プロジェクトを実行します。ピッキングアイテムに触れると消え、リーダーボードのプレイヤーのLockpicksの数が1つ増えることを確認します。

問題解決のヒント
すべての local 関数がモジュール関数の上にあることを確認します。モジュール関数は下にある local 関数を使用することができません。

現時点のTreasureManagerのスクリプト

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

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

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 のところに、TreasureManager.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

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

宝箱を開けたプレイヤーに宝物を与える関数を TreasureManager に作成します。

  1. TreasureManager に新しいモジュール関数を追加し、名前を openChest() とします。

パラメータは下記の2つです。

  • chestPart: 宝箱
  • whichCharacter:プレイヤー
------------------ 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)
end
  1. ピッキングアイテムの数を減らし、プレイヤーに宝物を与えるには下記のコードを openChest() にコピー&ペーストします。このコードでは chestReward などの先ほど作成した変数を使用します。この変数は、宝箱に含まれる宝物の量を表しています。
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() を作成したので、既成の partTouched() 関数を使用して、プレイヤーが宝箱に触れると呼び出されるようにすることができます。

  1. Workspace の Chests フォルダの中にある ChestScript スクリプトを開きます。
  2. treasureManager 変数にモジュールスクリプト(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 文の下にcanOpenという名前の新しい変数を作成します。treasureManagercanOpenChest() 関数を呼び出し、その結果を canOpen 変数に代入します。

treasureManager.canOpenChest(whichCharacter)

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. 次に canOpentrue かどうかを確認するために if 文を使用します。
  • trueである場合、TreasureManageropenChest() 関数を呼び出します。
  • openChest() 関数には、宝箱(chestPart)と、触れたプレイヤー(whichCharacter) の2つを渡します。
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. プロジェクトを実行し、次の2点を確認します。
  • 1つ以上のピッキングアイテムを持っていれば、宝箱を触ると消え、宝物が与えられる。
  • ピッキングアイテムを持っていなければ、宝箱を開けられない。

問題解決のヒント
ChestScriptで、canOpenChest() のようなモジュールスクリプトから呼び出される関数がTreasureManagerスクリプト内のスペルとまったく同じであることを確認します。1文字でも違うとエラーになります。
treasureManager.openChest() のようなコピー&ペーストされた関数がこのレッスンで表示されているものとまったく同じであることを確認します。少しでも違うとスクリプトで軽微なエラーが発生することがあります。

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

完成した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
        -- 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
 
-- Binds every chest part to the touch function so it works on all parts
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
        -- Give the player a lockpick and destroy the lockpick part
        -- =============================================
        treasureManager.getLockpick(lockpickPart, whichCharacter)
        -- =============================================
    end
end
 
-- Binds every lockpick part to the touch function so it works on all parts
for lockpickIndex = 1, #lockpicksArray do
    local lockpickPart = lockpicksArray[lockpickIndex]
    lockpickPart.Touched:Connect(function(otherPart)
        partTouched(otherPart, lockpickPart)
    end)
end

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です