サイトアイコン schilverberch★ROBLOX

【13】ポイントを与えるパーツを作成する

このプロジェクトでは、触ったときの色に応じてリーダーボード上でポイントを与えたり引いたりするパーツを作成します。青色であれば、プレイヤーに少しのポイントを与えます。緑色の場合は、多くのポイントを与えます。赤色であればポイントが奪われます。

プロジェクトを設定する

まずは、リーダーボードと色の変わるパーツを用意する必要があります。

リーダーボードを作成する

  1. ServerScriptServiceLeaderboard という名前の新しいスクリプトを作成します。
  2. 以下のコードをコピーして、Leaderboard のスクリプトに貼り付けてください。リーダーボードの作成方法はこちらのページでも解説しています。
-- ServerScriptServiceの中に作る
-- プレイヤーがゲームに入ってきた際に呼ぶ
 
local function onPlayerJoin(player)
    local leaderstats = Instance.new("Folder")
    leaderstats.Name = "leaderstats"
    leaderstats.Parent = player

    -- 整数値を保存するオブジェクトを作成
    local points = Instance.new("IntValue")
    points.Name = "Points"
    points.Value = 0
    points.Parent = leaderstats
 end
 
-- プレイヤーがゲームに入ってきた際に呼ばれる関数を接続する
game.Players.PlayerAdded:Connect(onPlayerJoin)

パーツとスクリプトを作成する

各色の変数を作成

各色とポイントの値は別の変数に格納されます。その後、変数をチェックしてポイントを与えたり引いたりすることができます。

  1. 固定パーツを1つ追加し、 PointPart という名前にします。それに PointScript という名前のスクリプトを追加します。
  2. このスクリプトの親(Parent)を pointPart 変数に格納します。
local pointPart = script.Parent
  1. 青を設定します。この色のパーツを取ると、プレイヤーは少ないポイントが貰えます。
    blue という名前の変数を作成し、Color3.fromRGB(0,0,255)に設定します。青はRGB値で(0,0,255)になります。
local pointPart = script.Parent
 
-- 青
local blue = Color3.fromRGB(0, 0, 255)
  1. 同様に、緑と赤の変数も作成します。緑はRGB値で(0,255,0)で、赤は(255,0,0)になります。
local pointPart = script.Parent
 
-- 青、緑、赤
local blue = Color3.fromRGB(0, 0, 255)
local green = Color3.fromRGB(0, 255, 0)
local red = Color3.fromRGB(255 ,0, 0)
  1. 次に3種類のポイント数の変数を作成します。
local pointPart = script.Parent
 
-- 青、緑、赤
local blue = Color3.fromRGB(0, 0, 255)
local green = Color3.fromRGB(0, 255, 0)
local red = Color3.fromRGB(255 ,0, 0)
 
-- ポイント 
local smallPoints = 10
local largePoints = 50
local losePoints = 100

プレイヤーサービスの追加

ポイントを付与するためには、ExplorerPlayers の中にあるプレイヤー情報にアクセスする必要があります。ここには、リーダーボードの統計情報などの情報が含まれています。(実行前はPlayersは空ですが、実行するとその中に自分のプレーヤー名が出てくるのが確認できると思います)

スクリプトにPlayersサービスを追加することで、プレイヤー情報にアクセスすることができます。サービスとは、Roblox のエンジニアが時間を節約するために作成した、事前に構築された機能の追加セットです。

-- ポイント 
local smallPoints = 10
local largePoints = 50
local losePoints = 100
 
-- 必要なサービス(game.Playersと同様)
local Players = game:GetService("Players")

サービス変数に名前をつける
他の変数は小文字から始まりますが、Players service のようなサービスは、サービスであることが分かるようにするために頭文字を大文字としています。

タッチ関数とポイント関数の設定

PointsScript には2つの関数が必要です。最初の関数は、ポイントを足したり、引いたりします。2つ目の関数は、プレイヤーがパーツにタッチしたかを確認します。

  1. givePoints() という名前の関数を作成します。パラメータはプレイヤーのオブジェクトにしますので、 player という名称にします。その中に、テストに使用するprint文を追加します。
local Players = game:GetService("Players")
 
-- ポイントの付与または減算
local function givePoints(player)
    print("Giving player points")
end
  1. その下にpartTouched() という名前の関数を作成し、パラメータ名は otherPart にします。
-- ポイントの付与または減算
local function givePoints(player)
    print("Giving player points")
 
end
 
-- プレイヤーが触れたかどうかの確認
local function partTouched(otherPart)
 
end
  1. 関数内で、GetPlayerFromCharacter(otherPart.Parent) を使用して、パーツに触れたプレイヤーを探します。GetPlayerFromCharacterのパラメーターはプレイヤーのModelになりますので、otherPart.Parentを渡します。
-- プレイヤーが触れたかどうかの確認
local function partTouched(otherPart)
    local player = Players:GetPlayerFromCharacter(otherPart.Parent)
end
  1. プレイヤーがパーツにタッチした場合、player 変数に格納されます。そうでない場合、変数は空のままになります。実際に確認してみましょう。
local function partTouched(otherPart)
    -- 触れたものがプレイヤーキャラのパーツの一部だった場合、Playerオブジェクトを返す
    local player = Players:GetPlayerFromCharacter(otherPart.Parent)
    if player then
        givePoints(player)
    end
end
 
pointPart.Touched:Connect(partTouched)

問題解決のヒント 
game:GetService(“Player”) の “Player ” の頭文字が大文字でになっており、引用符で囲まれていることを確認します。
Touched 接続がスクリプトの一番下になっていることを確認します。

ループする色を作成する

次から次へと色を変えるには、数秒ごとにパーツの色を変える while true do ループを使用します。

  1. スクリプトの最後に新しい while true do ループを作成します。
local function partTouched(otherPart)
    local player = Players:GetPlayerFromCharacter(otherPart.Parent)
    if player then
        givePoints(player)
    end
end
 
pointPart.Touched:Connect(partTouched)
 
-- Loops through colors
while true do
 
end

なぜループが最後なのですか?
while true do は無限ループになりますので、それより下のコードは実行されません。よって、while true do から抜け出ない場合は、それより下にコードを記述しても無意味になります。
ちなみに、while true do から強制的に抜け出す方法はあります。

  1. pointPart の色を変更する処理を行います。色を変更した後に、wait 関数を入れないとあまりにも早く色が変化してしまいゲームにならなくなりますので注意してください。
-- 青で3秒、緑で2秒、赤で1秒待つという流れをループする
while true do
    pointPart.Color = blue
    wait(3)
    pointPart.Color = green
    wait(2)
    pointPart.Color = red
    wait(1)
end
  1. テストプレイします。3秒間青に、2秒間緑に、1秒間赤に変わった後、また青に戻れば成功です。

問題解決のヒント
・whileループがスクリプトの一番下(Touchedイベントの下)にあることを確認します。ループが一番下ではないと、スクリプトの他の部分が正しく実行されなくなります。
Color3.fromRGB() 内の色が正しいことを確認します。0~255の3つの数字がカンマで区切られていなくてはいけません。例:(255, 50, 0)

プレイヤーにポイントを与える

パーツの色が変わるループを作ったので、次にプレイヤーにポイントを与える必要があります。色によって得られるポイントが違うので、if 文を使って、触れたときにどの色になっていたのかを確認し、色に応じたポイントを与えます。

プレイヤーのポイントとパーツの色を取得する

プレイヤーが適切なポイントを得られるようにする前に、プレイヤーが触れたときにそのパーツが何色だったかを、その時点でプレイヤーは何ポイント持っていたか確認する変数を作る必要があります。

  1. パーツの色は Color プロパティに入っていますので、currentColor 変数に代入します。
local function givePoints(player)
    local currentColor = pointPart.Color
end
  1. プレイヤーのリーダーボードを playerStats 変数に代入します。
local function givePoints(player)
    local currentColor = pointPart.Color
 
    local playerStats = player:WaitForChild("leaderstats")
end
  1. プレイヤーの Points 値を取得する変数を作成します。これはリーダーボードの子となります。
local function givePoints(player)
    local currentColor = pointPart.Color
 
    local playerStats = player:WaitForChild("leaderstats")
    local playerPoints = playerStats:WaitForChild("Points")
end

プレイヤーにポイントを与える

次に、パーツの色に基づき、異なる値の点数を与える if 文を入力します。

  1. if 文を使って、現在の色が青かどうかを確認し、青の場合は、smallPoints に含まれる数字をプレイヤーのポイントに加算します。playerPointsIntValue タイプなので、変数名の後に.Valueと入力することで数値が変更できます。
local function givePoints(player)
    local currentColor = pointPart.Color
 
    local playerStats = player:WaitForChild("leaderstats")
    local playerPoints= playerStats:WaitForChild("Points")
 
    if currentColor == blue then
        playerPoints.Value = playerPoints.Value + smallPoints
    end
 
end
  1. 次に elseif を使って、緑だった場合の処理をコーディングします。緑色は大きなポイントが与えれます。
if currentColor == blue then
    playerPoints.Value = playerPoints.Value + smallPoints
elseif currentColor == green then
    playerPoints.Value = playerPoints.Value + largePoints
end
  1. pointsPart が青でも緑でもない場合、つまり今回は赤になりますが、赤ということを確認せず、それ以外ということで else を使います。この場合はポイントが足されるのではなく減点になります。
if currentColor == blue then
    playerPoints.Value = playerPoints.Value + smallPoints
elseif currentColor == green then
    playerPoints.Value = playerPoints.Value + largePoints
else
    playerPoints.Value = playerPoints.Value - losePoints
end
  1. if 文の処理が終わった後は、パーツを破壊します。これを行わないと、パーツに触れている間中、ポイントが加算されたり減算されたりしてしまいます。パーツを消すことで、このスクリプトは1回だけ実行されるようになります。
if currentColor == blue then
    playerPoints.Value = playerPoints.Value + smallPoints	
elseif currentColor == green then
    playerPoints.Value = playerPoints.Value + largePoints
else
    playerPoints.Value = playerPoints.Value - losePoints	
end

pointPart:Destroy()
  1. テストプレイし、各色で正しいポイントが与えられるかを確認します。

すべての条件をテストする
複数の条件を持つ if 文を作った場合、すべての elseif および else 文が正常に動作することを確認するのがとても重要になります。1つの文だけテストして、すべてが正常に動作していると勘違いしてしまうと、後になって重大なバグとして発見され後悔してしまうかもしれません。

ここまでのPointScript

local pointPart = script.Parent

-- 青、緑、赤
local blue = Color3.fromRGB(0, 0, 255)
local green = Color3.fromRGB(0, 255, 0)
local red = Color3.fromRGB(255 ,0, 0)

-- ポイント
local smallPoints = 10
local largePoints = 50
local losePoints = 100

local Players = game:GetService("Players")

local function givePoints(player)
    local currentColor = pointPart.Color
 
    local playerStats = player:WaitForChild("leaderstats")
    local playerPoints= playerStats:WaitForChild("Points")

    -- パーツの色に応じてプレイヤーにポイントを付与
    if currentColor == blue then
        playerPoints.Value = playerPoints.Value + smallPoints	
    elseif currentColor == green then
        playerPoints.Value = playerPoints.Value + largePoints
    else
        playerPoints.Value = playerPoints.Value - losePoints	
    end

    pointPart:Destroy()
end
 
local function partTouched(otherPart)
    local player = Players:GetPlayerFromCharacter(otherPart.Parent)
    if player then
        givePoints(player)
    end
end
 
pointPart.Touched:Connect(partTouched)
 
-- パーツの色を変更する(スクリプトの一番下に記述する)
while true do
    pointPart.Color = blue
    wait(3)
    pointPart.Color = green
    wait(2)
    pointPart.Color = red
    wait(1)
end

プレイヤーにフィードバックを与える

PointPart は正しく動作していますが、プレイヤーはリーダーボードの数字が変化する様子だけが確認できるだけで、少し物足りなさが感じられます。PointPart が破壊された後に、プレイヤーを追跡する色のついたパーティクルを追加することができます。

プレイヤーがパーツに触れた時に、音や揺れ、パーティクルなどのフィードバックを追加することで、オブジェクト(パーツ)に触れることがより楽しくなることでしょう。

パーティクルエフェクトを作成する

パーティクルエフェクトは触れたときのパーツと同じ色にします。色を変数に保存しておいたのは、ここでもう一度使用するためです。各パーティクルは破壊されるまでプレイヤーに付きます。

  1. givePoints() で、パーツが破壊された後に新しい ParticleEmitter インスタンスを作成します。インスタンス名はコーテーションマーク内に表示されている通りのスペルにしてください。
    local particle = Instance.new("ParticleEmitter")
end
  1. どの色に触れたかプレイヤーが分かるようにするため、ParticleEmittercolor プロパティに新しい ColorSequence を設定します。これにより、放出される色が変わります。
-- パーツの消去
pointPart:Destroy()
 
-- パーティクルの作成
local particle = Instance.new("ParticleEmitter")
particle.Color = ColorSequence.new(currentColor)
  1. スクリプトがゲーム内でのプレイヤーの物理的な位置を把握できるようにするため、プレイヤーのキャラクターを使用します。キャラクターを格納するために新しい変数を作成し、player.Characterと同じとなるよう設定します。
    パーティクルの親(Parent)に、プレイヤーを設定する必要があります。playerCharacter でアクセスできるように、player.Character を設定しておきます。
local particle = Instance.new("ParticleEmitter")
particle.Color = ColorSequence.new(currentColor)
local playerCharacter = player.Character
  1. playerCharacter を使って、パーティクルを親(Parent)をプレイヤーの頭に設定します。これにより頭からパーティクルが出現するようになります。
local particle = Instance.new("ParticleEmitter")
particle.Color = ColorSequence.new(currentColor)
local playerCharacter = player.Character
particle.Parent = playerCharacter:WaitForChild("Head")

WaitForChild() によりランタイムエラーを防ぐ
Robloxのスクリプトは異なるタイミングで実行されるため、プレイヤーのヘッドがまだ作成されていない可能性があります。存在しないパーツを見つけようとするなどのエラーを避けるために、スクリプトではplayer.Head.Head のようにドット演算子を使うのではなく、WaitForChild() を使ってプレイヤーのキャラクターに取り付けられたヘッドパーツを取得しています。

  1. wait 関数を使用して、1秒後にパーティクルを破壊します。
local particle = Instance.new("ParticleEmitter")
particle.Color = ColorSequence.new(currentColor)
 
local playerCharacter = player.Character
particle.Parent = playerCharacter:WaitForChild("Head")
 
wait(1)
particle:Destroy()
  1. ゲームをテストし、各色にタッチした後、短時間プレイヤーを追跡するパーティクルが現れることを確認します。

問題解決のヒント
・新しいインスタンスを作成する際には ParticleEmitter が表示通りのスペルになっており、前後にコーテーションマークが付いていることを確認します。
particle.Parent を設定するときは、playerCharacterWaitForChild() の間に「:」を使用し、間にスペースが入っていないことを確認します。

完成したスクリプト

local pointPart = script.Parent
 
-- 青、緑、赤
local blue = Color3.fromRGB(0, 0, 255)
local green = Color3.fromRGB(0, 255, 0)
local red = Color3.fromRGB(255 ,0, 0)
 
-- ポイント
local smallPoints = 10
local largePoints = 50
local losePoints = 100
 
local Players = game:GetService("Players")
 
local function givePoints(player)
    local currentColor = pointPart.Color
 
    local playerStats = player:WaitForChild("leaderstats")
    local playerPoints = playerStats:WaitForChild("Points")
 
    -- パーツの色に応じてプレイヤーにポイントを付与
    if currentColor == blue then
        playerPoints.Value = playerPoints.Value + smallPoints	
    elseif currentColor == green then
        playerPoints.Value = playerPoints.Value + largePoints
    else
        playerPoints.Value = playerPoints.Value - losePoints	
    end
 
    -- パーツを消去
    pointPart:Destroy()

    -- スパークル効果を作成追加し、1秒後にそれを消去する
    local playerCharacter = player.Character
    local particle = Instance.new("ParticleEmitter")
    particle.Color = ColorSequence.new(currentColor)
    particle.Parent = playerCharacter:WaitForChild("Head")
    wait(1)
    particle:Destroy()
 
end
 
local function partTouched(otherPart)
    local player = Players:GetPlayerFromCharacter(otherPart.Parent)
 
    if player then
        givePoints(player)
    end
end
 
pointPart.Touched:Connect(partTouched)
 
-- パーツの色を変更する(スクリプトの一番下に記述する) 
while true do
    pointPart.Color = blue
    wait(3)
    pointPart.Color = green
    wait(2)
    pointPart.Color = red
    wait(1)
end

パーツをコピーして完成へ

パーツが1つだけではゲームになりません。完成した PointPart を好きなだけコピーして配置してみましょう。
また、そのままコピーしただけだと、一斉に色が変わってしまうので見た目も面白さもイマイチです。
そこで、while true doend 内の wait 関数の数値を変えてみましょう。それだけでも面白さが増すと思いますが、さらに一歩進み、プレイヤーが予測できないように不定期に色を変化させても良いかもしれません。
LUA言語の標準関数に、乱数を生成するものがあります。math.random(1,3) としますと、1、2、3のいずれかの数字が返ってきます。これを応用することで、下記のように不定期に色が変化するものが作成できます。

while true do
    pointPart.Color = blue
    wait(math.random(1,3))
    pointPart.Color = green
    wait(math.random(1,3))
    pointPart.Color = red
    wait(math.random(1,3))
end
モバイルバージョンを終了