いよいよ、この作業をまとめて行う時が来ました。ビームとパーティクルのコンポーネントを作成したところで、3つのスクリプトを追加します。これらのスクリプトは、コンポーネントにいつ何をすべきかを指示することで、チュートリアルを管理します。例えば、このスクリプトは、新しいプレイヤーにビームを発生させ、ゴールと接触するたびにパーティクルを発生させます。
コーディングの経験がなくても、これらのステップをすべて実行することができますが、その知識を持っていれば、これらのスクリプトを自分の経験に合わせてアレンジすることができます。さらに詳しく知りたい方は、コーディングの基礎をご覧ください。
ビームとパーティクルの保存
スクリプトを追加する前に、ビームとパーティクルを、スクリプトが必要に応じてコピーを作成できる場所に移動する必要があります。
独自のプロジェクトを使用する場合、スクリプト変数内の値をオブジェクトに合わせて置き換える必要があるかもしれません。
- ReplicatedStorageに、PlayerTutorialという名前の新しいフォルダーを作成します。TutorialBeam を TestPlayer から新しいフォルダーに移動します。
- ServerStorageに、TutorialParticles というフォルダを作成します。Burst パーティクルを TestPlayer からそのフォルダに移動します。
- ビームとパーティクルエミッターを動かしたら、TestPlayerは不要になります。このスクリプトが完成したら、本物のプレイヤーで動作するようになるので、TestPlayerを削除してください。
イベントの作成
プレイヤーがゴールを操作するたびに、スクリプトはそのプレイヤーの進捗を更新し、パーティクル効果を発することができるよう、知る必要があります。スクリプトに情報を伝えるには、イベントを使ってシグナルを送ります。
- ReplicatedStorage > PlayerTutorialで、2つの RemoteEvent オブジェクトを追加します。NextGoal と TutorialEnd と名付けます。
スクリプトを追加する
以下の3つのスクリプトは、作成済みのパーティクルエミッターとビームのオブジェクトを探し、チュートリアルシステムを管理します。
- 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
このスクリプトは、チュートリアルの進行状況を管理するためのコードを実行します。これには、ゴールと対話するためのコードの実行や、チュートリアルが終了したときの処理などが含まれます。
- 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という変数があり、インタラクション中に発生するパーティクルの数を決定します。
- 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)
- スクリプトをテストするためにプロジェクトをプレイする。ブースからブースへ移動し、インタラクト機能を使って、コードが動くかどうかを確認します。
トラブルシューティング
問題点:ゲーム開始時にパーティクルが再生される
- ServerStorage > Tutorial Particles > Burst のプロパティ Enabled がオフになっていることを確認します。
問題点:「infinite yield」というエラーが表示される
- スクリプトは特定の場所にある特定のオブジェクトを探すため、パーツの名前が間違っている可能性があります。ゲーム内の各パーツの名前と位置がチュートリアルと一致しているか再確認してください。
スクリプトの利点と限界
このチュートリアルシステムをゲーム上で使用する場合は、以下の点に注意してください。
メリット
- TutorialEnd などのイベントは、他のスクリプトを起動するために使用することができます。たとえば、このイベントが発生したときに、プレイヤーに特別なアイテムを与えることができます。
- TutorialParticles スクリプトは、一度に複数のパーティクルを再生することができます。ServerStorage/TutorialParticlesでさらにパーティクルを追加して、より複雑な効果を得ることができます。
制限事項
- チュートリアルの進行状況は持続しないので、何らかの方法で進行状況を保存する必要があります。
[ 作って遊ぼう ]