【Unity 2D】一番簡単なHPバーの作り方+その他ダメージ演出

よく作るのでメモしておきます。コード全体へのリンクは記事の最後に貼ってあります。

f:id:Kanchi0914:20200312213019g:plain

とりあえず一番簡単なHPバーの作り方

本当に最低限の労力でHPバーを作るなら以下のようになります。前提としてヒエラルキーCanvasが存在し、Render ModeがScreen Space-Cameraに設定されていることを想定しています。

  1. HierarchyのCanvas上で右クリック→UI>Panelを選択しオブジェクトを追加(減少したHPを表す部分)
  2. 適当な大きさに変更し、InspectorビューのImageコンポーネントのColorを調整(赤色で透明度を下げるなど)
  3. 2.のオブジェクトを複製し、複製したものを2.の子オブジェクトにしてColorを調整(HPの現在値を表す部分)
  4. Inspector上で3.のオブジェクトのRect Transform>PivotのXを0にする
  5. 3.のオブジェクトにスクリプトをアタッチし、RectTransformのScaleの値を0~1の値で変化させる(下記)

4.を行うことで、緑色のバーのオブジェクトのScaleの値を変更したとき、その左端を中心にオブジェクトの大きさが変化することになります。

例えばHPバーの対象となるオブジェクトが以下のように定義されているとします。

public class Human : MonoBehaviour
{
    private int maxHP = 100;
    private int currentHP = 100;
    
    public float PerHP
    {
        get
        {
            float value = (float)currentHP / (float)maxHP;
            return Mathf.Clamp(value, 0, 1);
        }
        private set { }
    }

    .....

PerHPは現在のHPの割合を0~1.0の値で返すプロパティです。Mathf.Clamp()は変数に上限値と下限値を設定し、値を超えていればそれらの設定した値を返してくれる便利なメソッドです。なおVisual Studio上では(float)CurrentHPのキャストが冗長だとされるのですが、付けないとint型のまま計算が行われ除算の結果が0か1にしかならなくなるので気を付けましょう。基本的な事ですが自分もよく忘れてバグを生みます。

3.で作った緑色のHPバーにアタッチするスクリプトは以下のように記述します。といってもUpdate内でRectTransformのScaleの値を変化させるだけです。

public class HPBar : MonoBehaviour
{
    [SerializeField]
    Human human;
    RectTransform rect;

    void Start()
    {
        rect = GetComponent<RectTransform>();
    }

    void Update()
    {
        rect.localScale = new Vector2(human.PerHP, 1f);
    }
}

f:id:Kanchi0914:20200312213009g:plain

とりあえずこれだけで見れるものは作れます。

ただこれだけだと物足りないので、今回はもう少し手を加えたものを作ります。

HPが減少したときだけバーを変化させる

上のコードのままだと、たとえば値が変化したときのみ処理を行うといったことができず、アニメーションなども付けられません。よってこれを現在のHPの値が変化したときのみ処理を行うように修正します。このような場合にはeventを使います。具体的には以下のようになります。

public class Human : MonoBehaviour
{
    public int MaxHP = 100;

    //デリゲートの宣言
    //増減した値を取得するために、変更前の値を引数としています
    public delegate void ValueChangedHandler(int preValue);
    //ここに対象の値が変化したときの処理を追加していく
    public event ValueChangedHandler HpChanged;

    public int CurrentHP
    {
        get { return currentHp; }
        set
        {
            if (currentHp != value)
            {
                var pre = currentHp;
                currentHp = value;
                //ここで登録したデリゲートが呼ばれる
                HpChanged(pre);
            }
        }
    }
    int currentHp = 100;

    .....

HPバーのスクリプトは以下のようになります。前に紹介したDOTweenを使用して簡単なアニメーションをつけています。

    void Start()
    {
        //HPが変化したときに呼びたいメソッドを追加
        //メソッドを複数追加することもできます
        human.HpChanged += OnHpChanged;
    }

    void OnHpChanged(int preValue)
    {
        transform.DOScaleX((float)human.PerHP, 0.2f);
    }

eventとはプロパティのデリゲート版ということで、デリゲートの呼び出しや追加・削除に制限を加えたものと自分的には認識しています。

ufcpp.net

実体はただのデリゲートなので、例えば上のコードからevent宣言を抜いても動作します。

    public delegate void ValueChangedHandler(int preValue);
    public ValueChangedHandler HpChanged;

何の原則というのかわかりませんが、実際にはバグを防ぐため機能に制約を設けた方がいいという考えがあるため、このような形になっているのだと思います。

なお今回、値の変更を検知する方法としてイベントを利用しましたが、他にもINotifyPropertyChangedを使うなどの実装方法があります。イベント自体にも色々問題があるらしく、Reactive Extensionsを使うなどしたほうが良いかもしれません。自分もそのうち勉強しようと思います。

ufcpp.net

その他

上の例だとHPバーを上のほうに表示していますが、HPをもつオブジェクトにCanvasを追加し、そこにHPバーオブジェクトを表示することで、キャラクターの動きにHPバーを追従させることもできます。

f:id:Kanchi0914:20200312213014g:plain

その他のダメージ表現などは全てDOTweenで実装しています。コード全体は以下に上げてあるのでよかったら参考にしてください。

github.com

お借りした素材:teddy-plaza様

teddy-plaza.sakura.ne.jp