[UNITY][Ruby’s Adventure]#6 パーティクル、UI(HPゲージ)

前回までの記事

この記事はUNITYの公式チュートリアル『Ruby's Adventure』を日本語で解説している記事です。

↓↓記事一覧↓↓

#1 UNITY導入~キャラクター移動

#2 タイルマップ~物理演算

#3 HP設定~回復とダメージ~敵の配置

#4 アニメーションの設定と適用

#5 攻撃とカメラ追従

#6 パーティクルとUI(HPゲージ) ←いまここ

#7 会話ダイアログ~音声~ビルド

(番外編)うまく動かない場合の対処法

パーティクル(エフェクト)を追加しよう

実際のチュートリアルページはこちら

パーティクルの素材を準備しよう

さて、続いてゲームの見栄えを良くするための、エフェクトを追加しましょう。

事前に準備された素材を使って、↓こんな感じのエフェクトを追加します。なお、エフェクトが出ている発生源を「Emitter」、放出されているエフェクトまたはエフェクト全体を「Particle」といいます。

さて、この動画をみて、あなたがゲームを開発する時のことを予感して

「え…こんなきれいな動画素材、自分では作れないよ…ツールも持ってないし…」

と思ったかもしれませんが、大丈夫です。パーティクルは意外なほど簡単に作ることができます。

まずはProjectウインドウ内のVFXフォルダからParticleSpriteAtlasという画像ファイルを選択し、InspectorウインドウでSprite Modeを「Multipe」に変更、Sprite EditorSliceGrid By Cell Countを選択し、4×4に設定してSliceApplyと進むことで、画像をスライスします。タイルパレットを作ったときと同じですね。

…なんとなく、簡単に作れる理由が解りましたか?

実は先ほどのパーティクル、たったこれだけの画像をコピペすることで作っているのです。

煙のパーティクルを作ろう

さて、まずは煙です。これは敵である壊れたロボットから常に出しておき、直ったら消えるようにします。

まずはHierarchyウインドウで右クリック→Effects→Particle Systemを作成し、「SmokeEffect」と名付けてください。作成すると、ゲーム画面に何やら白い煙のようなエフェクトが現れます。

まずはエフェクトを選択してInspectorウインドウ→Texture Sheet Animationのチェックを入れて、その項目を展開してください。

また、次の手順でパーティクルを編集してください。

  • ModeをSpriteに変更
  • 表示された枠の横にある+ボタンをクリックして、枠を2つにする
  • Projectウインドウの元画像の▶ボタンを押してスライス素材を展開し、煙の画像2種類を先ほどの枠にそれぞれアタッチする
  • Start Frameの右側にある▼ボタンをクリックしてRandom Between Two Constantsを選択、現れた枠に「0」と「2」を入力する(自動的に1.9998になりますがOKです)。
  • ウインドウの最下部に格納されている「Particle System Curves」をクリックしてカーブ図を展開し、「Remove」を押してカーブを削除する。

これで2種類のスプライトがランダムに発生するよう設定ができました。次に、煙が拡散しないように設定します。

同じくInspectorウインドウの「Shape」を展開し、Angle(角度)5に、Radius(半径)0にしてください(0にしようとすると0.0001にしかなりませんが、それでOKです。

次に、Inspectorウインドウの上部を見ていただき、次のように変更してください。

  • Start Lifetime消えるまでの時間)の▼ボタンから「Random Between Two Constants」を選択し、現れた二つの枠に「1.5」と「3」を入力
  • Start Size出現したときのサイズ)の▼ボタンから「Random Between Two Constants」を選択し、現れた二つの枠に「0.3」と「0.5」を入力
  • Start Speedパーティクルのスピード)の▼ボタンから「Random Between Two Constants」を選択し、現れた二つの枠に「0.5」と「1」を入力

以上で、↓良い感じになりました。

こんな風になりました。

次に、煙が徐々にフェードアウトするように設定します。

Inspectorウインドウ下部からColor over Lifetimeにチェックを入れてから展開し、白い部分をクリックしてGradient Editorを開きます。

左側がパーティクル出現時、右側が消失時の透明度/色なので、右側の上にあるマークを選択し、Alpha値を0にしてください。↓

↑こんな感じです。

次に、煙が徐々に小さくなるようにします。

同じく並んでいる「Size Over Lifetime」のチェックを入れて展開し、中にあるカーブをクリックします。

すると下のParticle System Curvesの中に曲線が表示されるので、↓こんな風に編集してください。

↑左の頂点を上に、右の頂点を下に移動させ、右の頂点にくっついてるアンカーを移動させる感じです。半分くらいまでまっすぐ、そこから下がるような感じでOKです。

以上で煙パーティクル自体は完成です。なお、Texture Sheet Particleについてはこの記事が詳しいです。

パーティクルを敵に配置しよう

さて、次はこの煙を敵ロボットに配置しましょう。

まずはこれまで作ってきたSmokeEffectオブジェクトをProjectウインドウのPrefabフォルダ内にドラッグ&ドロップしてPrefab化して、オブジェクトのほうは削除してください。

次に、敵のPregab編集画面を開き、Hierarchyウインドウ内に煙Prefabをドラッグ&ドロップしてください。

これで敵Prefabに煙パーティクルがくっつきました。あとはWキーで位置を調整すればOKです。

なお、煙が敵よりも前にきてしまう場合は煙のZ軸0にしてください。

さて、これで試動してみると…なんか違和感がありますね。

敵が移動すると、吐き出された煙も敵に付いていってしまっています。

そこで、吐き出された煙がその場に留まるようにします。

SmokeEffectのPrefab編集モードで、InspectorウインドウからSimulation SpaceWorldに変更してください。

これだけで完了です。簡単ですね。

 

続いて、敵が直った時に煙を止めるように設定します。

EnemyControllerスクリプトを開き、最初のほうに並んでいる定義に次の定義を追加します。

public ParticleSystem smokeEffect;

ParticleSystem」と書くと、Inspectorウインドウでパーティクルのオブジェクトをアタッチできるようになります。

次に、Fix()内の一番下に次のコードを追加してください。

smokeEffect.Stop();

Stop()」と書くと、アタッチしたパーティクルを止められます。

ちなみに、歯車を消した時のように「Destroy」ではダメなのでしょうか?

Destroy」はオブジェクト自体を消滅させるコードなので、吐き出された煙ごと消えてしまいます。「Stop()」と書けばパーティクルが止まるだけなので、吐き出された煙は残ってくれるわけですね。

スクリプトを保存してUNITY画面に戻り、敵のPrefab編集モードで、HierarchyウインドウからSmokeEffectオブジェクトをInspectorウインドウの「Smoke Effect」にアタッチしてください。

このとき、Projectウインドウ内のPrefabからアタッチすると(なぜか)正常に動作しませんので注意です。

 

以上で煙パーティクルは完成です。

スーパー・クリエイティブ・タイム!

さて、元のチュートリアルページを見ると、「他のパーティクルを自由に追加してみよう!」で説明が終わっています。

…が、ちょっと難易度が高すぎて公式フォーラムが大荒れなので、作り方の一例を記載します。

とりあえず、「被ダメージ時のパーティクル」と「HP回復パーティクル」を作って、さらにHP回復パーティクルは「敵に歯車が当たった時」にも同じものが発生するよう設定したいと思います。

まずは被ダメージ時のパーティクルですが、次のように作ってください。

  • Hierarchyウインドウでカラのパーティクルを作成、「Hit Effect」とでも名付ける
  • Durationは「1」
  • Loopをオフ
  • Start Life TimeはRandom Between Two Constantsにして「0.1」「0.2」
  • Start Speedは「0」
  • Start SizeはRandom Between Two Constantsにして「0.7」「1」
  • Start RotationはRandom Between Two Constantsにして「0」「360」
  • Emitter Velocity ModeはTransform
  • Stop ActionはDestroy
  • CullingはAlways Simulate
  • EmissionRate Over Timeは「0」
  • EmissionBurstsに+ボタンで項目追加し、「0」「4」「1」「0.01」「1」
  • ShapeShapeはCircle
  • ShapeRadiusは0.12
  • ShapeArcModeはBurst Spread
  • Color Over Lifetimeをオン
  • Color Over Lifetimeを右上のAlpha値を「0」にして、上の段の80%の位置にもう一つ中間点を配置してAlpha値を「255(最大)」にする
  • Size Over Lifetimeをオン
  • Size Over Lifetimeのカーブは左の頂点が「1(一番上)」の位置、右の頂点が「0(真ん中)」の位置
  • Texture Sheet AnimationModeはSprite、枠に煙の時にスライスした画像のなかのオレンジの爆発みたいなスプライトをアタッチ
  • RendererOrder in layerを「10」
  • Projectウインドウ内のPrefabフォルダにドラッグ&ドロップしてPrefab化
  • Hierarchyウインドウのオブジェクトを削除

次に、HP回復パーティクルを次のように作ってください。

  • Projectウインドウ内のPrefabフォルダの先ほど作った「Hit Effect」をCtrl+Dで複製、「Health Pickup」とでも名付ける
  • Texture Sheet Animationの画像を入れた枠の+ボタンを2回押して枠を3つにして、煙の時にスライスした画像のなかの黄色い星が散ってるような画像3つをそれぞれにアタッチ
  • Start Life Timeの数字は「0.5」「1」
  • Start Speedは「0.6」「1.5」
  • Start Sizeは「0.1」「0.5」
  • EmissionBurstsCountは「20」
  • ShapeShapeはSphere
  • ShapeArcModeはRandom
  • Velocity over Lifetimeにチェックを入れる
  • Velocity over LifetimeSpeed ModifierをCurveに変更、その曲線は右の頂点を「0」(真ん中)の位置に移動。アンカーはいじらない。
  • Color Over Lifetimeの右上のAlpha値を「62」に変更
  • Size Over Lifetimeのカーブは左の頂点が「1」(一番上)、右の頂点が「0.5」(真ん中のちょっと上)くらいの位置。
  • Rotation Over Lifetimeにチェックを入れる
  • Rotation Over LifetimeAngular Velocityは「90」

で、次はRubyControllerスクリプトを編集します。まず、スクリプトの上のほうに並んでいる定義に次の定義を追加します。

public ParticleSystem hitEffect;
public ParticleSystem healthPickup;

それぞれのパーティクルをInspectorウインドウでアタッチするための変数です。

次に、ChangeHealth()内の「if (amount < 0)」の中の最後に次のコードを追加します。

ParticleSystem Damage = Instantiate(hitEffect, rigidbody2d.position + Vector2.up * 0.5f, Quaternion.identity);

さて、これは被ダメージ時のパーティクルを表示させるコードです。

Damage」は計算を入れるための変数ですが、他では使いません。

Instantiate」はオブジェクトを出現させるコードでしたね。敵の煙パーティクルはシーン開始時から常に出ている状態だったので必要ありませんでしたが、被ダメージ時のパーティクルとHP回復パーティクルは初めは非表示なのが途中で表示されるようにしたいので、Instantiateを使っています。

Instantiateの引数は(生成するオブジェクト位置回転)でしたね。括弧()内の「hitEffect」は、先ほど定義しましたね。Inspectorウインドウでパーティクルを入れる予定の変数です。

位置として設定されている「rigidbody2d.position + Vector2.up * 0.5f」は、Rubyの上半身あたりです。歯車の時と同じですね。

Quaternion.identity」は、これも歯車の時と同じです。「回転していない」という命令のできるコードですね。

 

続いて、ChangeHealth()内の最後(if文の中ではなく、メソッドの最後)に次のコードを追加します。

if (amount > 0)
{
    ParticleSystem Heal = Instantiate(healthPickup, rigidbody2d.position + Vector2.up * 0.5f, Quaternion.identity);
}

こちらはHP回復パーティクルを表示させるコードです。

被ダメージ時と逆に「if (amount > 0)」と書いてあるので、HPが増えた時に処理されます。

内容は被ダメージ時と同じですね。

 

続いて、EnemyControllerスクリプトの上のほうの定義にこれを追加、

public ParticleSystem cogFixed;

Fix()内の最後に次のコードを追加してください。

ParticleSystem Heal = Instantiate(cogFixed, rigidbody2D.position + Vector2.up * 0.5f, Quaternion.identity);/* Your code... */

ほとんど同じですね。ポイントとしては、Ruby側で「healthPickup」という変巣をpublicで定義していたので、同じ変数名を使って何らかのエラーが起きたら恐いので「cogFixed」と変えています。さらに、このスクリプトではRigidbody 2Dを取得する変数名がちょっと違ったのでそこも直しています(2d→2D)。

 

以上を保存してUNITY画面に戻り、Rubyと敵のPrefabのInspectorウインドウにそれぞれ生成されている枠に、Projectウインドウから2つのパーティクルPrefabをドラッグ&ドロップすれば完了です!

[ad]

UIを追加しよう

実際のチュートリアルページはこちら

HPゲージの見た目を準備しよう

さて、次はいよいよUIを追加しましょう。どんどんゲームっぽくなっていくのでワクワクしますね!

さて、まずはRubyのHPゲージを作りたいと思います。

↑こんなの

まず、Hierarchyウインドウ内で右クリック→UI→Canvasを作成してください。

次に、Hierarchyウインドウ内のCanvasを右クリック→UI→Imageを作成してください。これでオブジェクトCanvas内に画像UIが作成されました。

Imageをダブルクリックして場所を確かめると、こんな風に巨大なものが生成されていることが解ります。これで大丈夫です。

まずはそのImage内のSource Imageに、ProjectウインドウのSprites→UIフォルダにあるHPゲージ画像をアタッチしてください。

↑こんな感じ

潰れてしまっているので、同じくInspectorウインドウ内にあるSet Native Sizeボタンを押してください。

とんでもないことになりましたね。大丈夫です。

Tキーを押して矩形ツールに切り替え、Shiftキーを押しながら画像を縮小し、Wキーを押してCanvasの左上のほうに配置してください。

↑こんな感じ

 

で、この状態でウインドウサイズを調整して画面サイズが小さいプレイ環境を確認してみると、

こんな風にUIがハミ出てしまいましたね。

…いや、私はハミ出なかったのですが、元のチュートリアルでは「ハミ出ましたよね」って書いてあるので、きっとハミ出た人もいるのだと思います。

なので、UIのアンカー(移動・サイズ変更の基準点)を左上隅にしたいと思います。Imageを選択した状態でInspectorウインドウ内のRect Transformのなかの四角形の図形みたいなものをクリックし、左上の四角形を選びます。

↑これです。

↑いい感じになりました。

あ、「Image」だと解りにくいので、オブジェクト名を「Health」にリネームしておきましょう。

 

さて、次はHierarchyウインドウでHealthを右クリックしてさらにImageを作成し、「PlayerPortrait」と名付けましょう。

で、そのSource ImageにProjectウインドウのUIフォルダにあるRubyの顔の画像をアタッチして、青い丸のなかに配置します。

↑それっぽくなりました。

で、この状態でサイズを調整してみると…

あら、また変ですね。

これも、アンカーを調整する必要があります。今度はアンカーの基準点ではなく、枠を調整しましょう。

先ほど同様にアンカーの四角形をクリックし、今度は右下の四角形をクリックすると、アンカーを点ではなく枠で調整できるようになりますので、▽マークを青い丸のギリギリまで調整してみましょう。

↑こんな感じです。良い感じですね。

 

続いて、HPゲージの中身を配置します。青い画像を用意して、拡大縮小する方法だとどうでしょうか?

うーん、縮小したとき、明らかに「画像が潰れてる感」が出てしまってかっこ悪いですね…。

そのため、今回はマスク機能を使います。

先ほどと同じく、オブジェクトHealthのなかにUIのImageを作成し、「HealthBarMask」と名付け、サイズをHPゲージぴったりに調整、アンカーの枠も同じく調整します。

↑白いままで大丈夫です。

次に、Pivotを動かします

↑左中央に配置。

で、そのオブジェクトにコンポーネント「Mask」を追加し、Show Mask Graphicをオフにしてください。

次に、その「HealthBarMask」オブジェクトのなかに、さらにUIのImageを作成、「HealthBar」と名付けます。

↑こういう階層になります。

で、そのHealthBarのSource Imageに、ProjectウインドウのSprites→UIフォルダにある「UIHealthBar」という画像をアタッチしてください。

ここから少々解りにくいので、一つ一つ確認しながら進めてください。

  • HealthBar」を選択した状態で、Inspectorウインドウのアンカー編集ボタンを押します。
  • Altキーを押しながら、右下の四角形をクリックします。すると、HPゲージ画像がマスク(親オブジェクト)のサイズに最適化されます。
  • 次に、Altキーを離して、左中央の四角形を選択します。

動画で説明するとこんな感じです↓

これで完成です。

この状態でサイズを変更してみましょう。

良い感じですね!

HPゲージのスクリプトを作ろう

さて、次にHPゲージ用のスクリプトを作りましょう。

Scriptsフォルダ内に「UIHealthBar」という名前のC#スクリプトを作成し、Healthオブジェクトにアタッチして、内容を次のように書き換えてください。

using UnityEngine.UI;

public class UIHealthBar : MonoBehaviour
{
    public static UIHealthBar instance
    {
        get;
        private set;
    }

    public Image mask;
    float originalSize;

    void Awake()
    {
        instance = this;
    }

    void Start()
    {
        originalSize = mask.rectTransform.rect.width;
    }

    public void SetValue(float value)
    {
        mask.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, originalSize * value);
    }
}

初めに、最も大切かつ忘れやすいのが「using UnityEngine.UI;」です。UIに関する処理をしたい場合は最初のusingにこれを加える必要があるので、覚えておきましょう。

public static UIHealthBar instance { get; private set; }」ですが、「static」についてはこの記事が大変解りやすいですが、要するにこのスクリプトを別のスクリプト(RubyController)から呼び出したいので「static」と書いています。

「他のスクリプトからメソッドを呼び出す」というテクニックを、実はあなたは既に経験しています。それは「Time.deltaTime」です。

「Time.deltaTime」はUNITYが事前に用意している「Time」というクラスのなかに、「deltaTime」という変数がある、という程度に覚えておけばよいでしょう。

より詳しく知りたいかたはこちらの記事がわかりやすいです。

↑で作った「instance」に、Awake()で「this」を入れています。「this」は「このクラス(スクリプト)」という意味です。つまり、自分(this)をstaticな存在にすることで、どこからでもUIHealthBarというクラス(スクリプト)にアクセスできるようにしているのですね。

public Image mask;」で画像をInspectorウインドウでアタッチするための変数を定義します。

float originalSize;」はサイズを変更するための変数ですね。

 

Start()で「originalSize = mask.rectTransform.rect.width;」と書くことで、先ほどの変数で矩形(rect)サイズ変更させられるようにしています。

public void SetValue(float value)」で、値をfloat型で渡せるメソッドを書いています。渡した値でサイズが変わります。
では、この「value」とはなにかというと、これは「(何を渡すかを解りやすくするために)引数につけた名前」なので、実は何でもよいです。「float aiueo」とかでも動きます。

その内容ですが、「mask.rectTransform.SetSizeWithCurrentAnchors」はアンカーを基準としたサイズ変更ですね。次の「(RectTransform.Axis.Horizontal, originalSize * value)」で「Horizontal」と書いてあるので、横だけのサイズ変更になります。

 

スクリプトを書き終えたら、Healthオブジェクトにアタッチしたこのコンポーネントの「Mask」にHealthBarMaskオブジェクトをアタッチしてください

 

続いて、RubyControllerスクリプトのChangeHealth()内の最後に次のコードを書き加えてください。

UIHealthBar.instance.SetValue(currentHealth / (float)maxHealth);

UIHealthBar.instance.SetValue」ですが、

UIHealthBar」は先ほどのスクリプト(クラス)ですね。

instance」はそのスクリプト内で定義した変数で、そのクラス自体を入れていましたね。

SetValue」で、括弧()内の値を入れてメソッドを呼び出しています。

その値は「currentHealth / (float)maxHealth」とあり、これは「現在HP÷最大HP」です。HPが最大の時にダメージを受けると、「4/5」となり、ゲージのサイズが5分の4になるというわけです。

 

以上で完了です!お疲れ様でした!

次の記事はこちらです

#7 会話ダイアログ~音声~ビルド

 

ここまでに完成したスクリプトの例