B-E4 スクリプトの追加

いよいよ、この作業をまとめて行う時が来ました。ビームとパーティクルのコンポーネントを作成したところで、3つのスクリプトを追加します。これらのスクリプトは、コンポーネントにいつ何をすべきかを指示することで、チュートリアルを管理します。例えば、このスクリプトは、新しいプレイヤーにビームを発生させ、ゴールと接触するたびにパーティクルを発生させます。

https://prod.docsiteassets.roblox.com/assets/education/build-it-play-it-mansion-of-wonder/adding-scripts/final-example.mp4

コーディングの経験がなくても、これらのステップをすべて実行することができますが、その知識を持っていれば、これらのスクリプトを自分の経験に合わせてアレンジすることができます。さらに詳しく知りたい方は、コーディングの基礎をご覧ください。

ビームとパーティクルの保存

スクリプトを追加する前に、ビームとパーティクルを、スクリプトが必要に応じてコピーを作成できる場所に移動する必要があります。

独自のプロジェクトを使用する場合、スクリプト変数内の値をオブジェクトに合わせて置き換える必要があるかもしれません。
  1. ReplicatedStorageに、PlayerTutorialという名前の新しいフォルダーを作成します。TutorialBeam を TestPlayer から新しいフォルダーに移動します。
  1. ServerStorageに、TutorialParticles というフォルダを作成します。Burst パーティクルを TestPlayer からそのフォルダに移動します。
  1. ビームとパーティクルエミッターを動かしたら、TestPlayerは不要になります。このスクリプトが完成したら、本物のプレイヤーで動作するようになるので、TestPlayerを削除してください。
https://prod.docsiteassets.roblox.com/assets/education/build-it-play-it-mansion-of-wonder/adding-scripts/delete-test-player.mp4

イベントの作成

プレイヤーがゴールを操作するたびに、スクリプトはそのプレイヤーの進捗を更新し、パーティクル効果を発することができるよう、知る必要があります。スクリプトに情報を伝えるには、イベントを使ってシグナルを送ります。

  1. ReplicatedStorage > PlayerTutorialで、2つの RemoteEvent オブジェクトを追加します。NextGoal と TutorialEnd と名付けます。

スクリプトを追加する

以下の3つのスクリプトは、作成済みのパーティクルエミッターとビームのオブジェクトを探し、チュートリアルシステムを管理します。

  1. ReplicatedStorage > PlayerTutorial > TutorialManager という名前の新しい ModuleScript を作成します。

以下のコード全体をコピー&ペーストして、デフォルトのコードを置き換えてください。

local TutorialManager = {}

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local tutorialFolder = ReplicatedStorage:WaitForChild("PlayerTutorial")
local TutorialEndEvent = tutorialFolder:WaitForChild("TutorialEnd")
local NextGoalEvent = tutorialFolder:WaitForChild("NextGoal")

-- Note Goal parts must be ordered in the table, or else Goal order may be different in-game
local goalParts = {
    workspace.TutorialGoals.GoalPart1,
    workspace.TutorialGoals.GoalPart2
}

local function checkTutorialEnd(player, goalParts)
    local currentIndex = player:WaitForChild("GoalProgress")
    return currentIndex.Value >= #goalParts
end

local function finishTutorial(player)
    local playerBeam = player.Character.HumanoidRootPart:FindFirstChildOfClass("Beam")
    playerBeam:Destroy()

    print(player.Name .. " finished the tutorial")

    -- Placeholder for further code. E.g. if you wanted to send messages to the server to do other tasks
end

function TutorialManager.interactGoal(player)
    NextGoalEvent:FireServer()
end

function TutorialManager.getTutorialGoals()
    return goalParts
end

function TutorialManager.nextGoal(player, goalParts)
    if checkTutorialEnd(player, goalParts) then
        finishTutorial(player)
    else
        -- Increment the player's Goal tracker
        local currentGoalIndex = player:WaitForChild("GoalProgress")
        currentGoalIndex.Value += 1
    end
end

-- Creates an int value to locally track player's progress through the tutorial Goals
function TutorialManager.setupPlayerProgress(player)
    local currentGoalProgress = Instance.new("IntValue")
    currentGoalProgress.Name = "GoalProgress"
    currentGoalProgress.Value = 1
    currentGoalProgress.Parent = player
end

return TutorialManager

このスクリプトは、チュートリアルの進行状況を管理するためのコードを実行します。これには、ゴールと対話するためのコードの実行や、チュートリアルが終了したときの処理などが含まれます。

  1. ServerScriptService に、TutorialParticles という名前の新しい Script を作成します。

以下のコードを貼り付けてください。

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local tutorialFolder = ReplicatedStorage:WaitForChild("PlayerTutorial")
local NextGoalEvent = tutorialFolder:WaitForChild("NextGoal")

local EMIT_RATE = 50

local function playParticleBurst(player)
    local character = player.Character or player.CharacterAdded:Wait()
    local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
    local particleAttachment = humanoidRootPart:WaitForChild("ParticleAttachment")

    -- Go through particles on the attachment and play them according to the type of particle
    for _, particle in pairs(particleAttachment:GetChildren()) do
        if particle:IsA("ParticleEmitter") then
            particle:Emit(EMIT_RATE)
        end
    end
end

local function setupPlayerParticles(player)
    player.CharacterAdded:Connect(function(character)
        local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
        local playerParticleAttachment = Instance.new("Attachment")
        playerParticleAttachment.Name = "ParticleAttachment"
        playerParticleAttachment.Parent = humanoidRootPart

        -- Clone particles in the folder, even if there are more than one and attach to player
        for _, emitter in ipairs(ServerStorage.TutorialParticles:GetChildren()) do
            emitter:Clone().Parent = playerParticleAttachment
        end
    end)
end

Players.PlayerAdded:Connect(setupPlayerParticles)
NextGoalEvent.OnServerEvent:Connect(playParticleBurst)

このスクリプトは、プレイヤーがゴールとインタラクションするたびに、バーストパーティクルを再生します。また、EMIT_RATEという変数があり、インタラクション中に発生するパーティクルの数を決定します。

  1. StarterPlayer > StarterPlayerScriptsで、TutorialScript という名前の新しい LocalScript を作成します。

そして、以下のスクリプトを貼り付けてください。このスクリプトは、プレイヤーを誘導するためのビームを作成し、管理するものです。

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local tutorialFolder = ReplicatedStorage:WaitForChild("PlayerTutorial")
local TutorialManager = require(tutorialFolder:WaitForChild("TutorialManager"))
local TutorialEndEvent = tutorialFolder:WaitForChild("TutorialEnd")

local player = Players.LocalPlayer
local goalParts = TutorialManager.getTutorialGoals()
local playerBeam = nil
local goalIndex = nil

local function getTargetAttachment()
    local currentTarget = goalParts[goalIndex.Value]
    local interactionPart = currentTarget:FindFirstChild("InteractionPart")
    local attachment = interactionPart:FindFirstChildOfClass("Attachment")

    if not attachment then
        attachment = Instance.new("Attachment")
        attachment.Name = "BeamAttachment"
        attachment.Parent = currentTarget
    end

    return attachment
end

local function updateBeamTarget()
    playerBeam = player.Character.HumanoidRootPart:FindFirstChildOfClass("Beam")

    local targetBeamAttachment = getTargetAttachment()

    if targetBeamAttachment then
        playerBeam.Attachment1 = targetBeamAttachment
    else
        warn("Attachment not found in a goal. Check that goals have attachments or they're included under the InteractionPart")
    end
end

local function setupGoals()
    for _, part in ipairs(goalParts) do
        local interactionPart = part:FindFirstChild("InteractionPart")
        local proximityPrompt = interactionPart:FindFirstChild("ProximityPrompt")

        if proximityPrompt then
            proximityPrompt.Triggered:Connect(function(player)
                proximityPrompt.Enabled = false
                TutorialManager.nextGoal(player, goalParts)
                TutorialManager.interactGoal(player)
            end)
        else
            warn("Proximity prompt not included in goal. Add one to each goal part under the InteractionPart")
        end
    end
end

local function createBeamForCharacter(character)
    local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
    local playerBeamAttachment = Instance.new("Attachment")
    local beamTemplate = tutorialFolder:WaitForChild("TutorialBeam")

    if not beamTemplate then
        warn("Tutorial Beam not found in ReplicatedStorage")
    end

    playerBeamAttachment.Name = "BeamAttachment"
    playerBeamAttachment.Parent = humanoidRootPart

    local targetBeamAttachment = getTargetAttachment()

    playerBeam = beamTemplate:Clone()
    playerBeam.Attachment0 = playerBeamAttachment
    playerBeam.Attachment1 = targetBeamAttachment
    playerBeam.Parent = humanoidRootPart
    playerBeam.Enabled = true
end

local function setupPlayer()
    setupGoals()
    TutorialManager.setupPlayerProgress(player)
    goalIndex = player:WaitForChild("GoalProgress")

    player.CharacterAdded:Connect(createBeamForCharacter)
    if player.Character then
        createBeamForCharacter(player.Character)
    end
end

setupPlayer()
goalIndex.Changed:Connect(updateBeamTarget)
  1. スクリプトをテストするためにプロジェクトをプレイする。ブースからブースへ移動し、インタラクト機能を使って、コードが動くかどうかを確認します。
https://prod.docsiteassets.roblox.com/assets/education/build-it-play-it-mansion-of-wonder/adding-scripts/final-example-2.mp4

トラブルシューティング

問題点:ゲーム開始時にパーティクルが再生される

  • ServerStorage > Tutorial Particles > Burst のプロパティ Enabled がオフになっていることを確認します。

問題点:「infinite yield」というエラーが表示される

  • スクリプトは特定の場所にある特定のオブジェクトを探すため、パーツの名前が間違っている可能性があります。ゲーム内の各パーツの名前と位置がチュートリアルと一致しているか再確認してください。

スクリプトの利点と限界

このチュートリアルシステムをゲーム上で使用する場合は、以下の点に注意してください。

メリット
  • TutorialEnd などのイベントは、他のスクリプトを起動するために使用することができます。たとえば、このイベントが発生したときに、プレイヤーに特別なアイテムを与えることができます。
  • TutorialParticles スクリプトは、一度に複数のパーティクルを再生することができます。ServerStorage/TutorialParticlesでさらにパーティクルを追加して、より複雑な効果を得ることができます。
制限事項
  • チュートリアルの進行状況は持続しないので、何らかの方法で進行状況を保存する必要があります。

コメントを残す コメントをキャンセル

モバイルバージョンを終了