ターン制RPGの作り方#3|仲間のHPとMPを表示するステータスのウインドを作ろう

右下のウインドに、スライムちゃん(HP 38/38・MP 8/8)・フェアリー(26/26・20/20)・キョンシー(32/32・6/6)の3行が、緑のHPゲージと青のMPゲージ付きで並んでいる

RPGのバトルで一番よく見る場所といえば、仲間のHPとMPが並んだウインドですよね。「あと何回攻撃を受けても大丈夫か」「まほうを使えるMPは残っているか」が、ひと目で分からないと戦えません。今回は右下のウインドに、仲間3人の名前・レベル・HP・MPを、ゲージと数字の両方で見せるステータスのウインドを作ってみたいと思います!

目次

今回作るもの

  • HPやMPを、ゲージと「38/38」のような数字で見せるGauge
  • 1人分の名前・Lv・HP・MPを並べた行StatusRow(プレハブ)
  • データから3人分の行を作るStatusWindow
スクリプト役割付ける場所
Gaugeゲージの長さと数字を変えるHpGauge・MpGauge
StatusRow1人分の行の表示StatusRowのプレハブ
StatusWindowデータから行を作るStatusWindow
BattleLineup並べるついでに、ステータスの行も作る(#2のものを書き換え)Battle

使うスタートガイドの記事は次の2つです。

1人分の行を作ろう

まずは、1人分の行の見た目を組み立てます。完成すると、次のような1行になります。

左から「スライム」「Lv1」、緑のゲージの中に「HP」と「38/38」、青のゲージの中に「MP」と「8/8」が並んだ1行

StatusWindowの子に、空のGameObject「StatusRow」を作ります。中身は、横に4つ並べます。

  • StatusRowにAdd Component>Horizontal Layout Group
    • Spacing:24、Child Alignment:Middle Left
    • Control Child Size:Width・Heightにチェック、Child Force Expand:Heightだけチェック
  • StatusRowの子に、次の4つを作る
名前作り方中身幅(Layout Element の Preferred Width)
NameTextText – TextMeshPro「スライム」、Font Size 34、太字260
LevelTextText – TextMeshPro「Lv1」、Font Size 30120
HpGaugeImage(下で作る)HPのゲージ520
MpGaugeImage(下で作る)MPのゲージ360
  • 4つともAdd Component>Layout Elementを付けて、Preferred Widthに表の幅を入れる
    • Horizontal Layout Groupは、子のLayout Elementを見てそれぞれの幅を決めてくれるので、数字だけで並びを調整できます

4つの幅と間隔を足すと 260+120+520+360+24×3=1332。StatusWindowの幅(1380)から、左右の余白24ずつを引いた幅にちょうど収まるようにしてあります。

Gaugeを作ろう

HPのゲージは、4つの部品でできています。

  • HpGauge(UI (Canvas)>Image)
    • Source Image:ui_white、Color:#1A1A2E(ゲージの背景の暗い色)
  • HpGaugeの子にBack(UI (Canvas)>Image)
    • アンカー:stretch-stretch、Left・Top・Right・Bottom:4(背景のふちを少し見せる)
    • Source Image:ui_white、Color:#4A4A66(灰色がかった紺。ゲージの最大の長さを見せる帯)
    • Raycast Targetのチェックを外す
  • HpGaugeの子にFill(UI (Canvas)>Image)。Backの下(Backより手前に描かれる)に置く
    • アンカー:stretch-stretch、Left・Top・Right・Bottom:4(Backとぴったり同じ大きさ)
    • Source Image:ui_white、Image Type:Filled、Fill Method:Horizontal、Fill Origin:Left
    • Color:#3FAE5A(緑)
  • HpGaugeの子にLabel(Text – TextMeshPro)
    • アンカー:stretch-stretch、Left:16、Top・Right・Bottom:0
    • 「HP」、Font Size:24、太字、Alignment:左寄せ・上下中央
  • HpGaugeの子にValue(Text – TextMeshPro)
    • アンカー:stretch-stretch、Left:0、Top・Bottom:0、Right:16
    • 「38/38」、Font Size:28、太字、Alignment:右寄せ・上下中央
HpGauge>FillのInspector。アンカーがstretch-stretchでLeft・Top・Right・Bottomが4、ImageのSource Imageがui_white、Colorが緑、Image TypeがFilled、Fill MethodがHorizontal、Fill OriginがLeft

ゲージの絵には、角の丸い画像ではなく白い四角(ui_white)を使います。Filledは画像を切り分けずにそのまま縮めるので、角の丸い画像だとゲージの両端が丸くつぶれて、見苦しくなります。色はImageのColorで付けるので、白い四角が1枚あれば、HPにもMPにも使えます。

Backは、Fillと同じ大きさの帯です。HPが減ってFillが縮むと、その後ろのBackが見えて、「全体のうち、どれだけ残っているか」がひと目で分かります。Backが無いと、減ったところが背景と同じ色になって、ゲージの最大の長さが分かりにくくなります。

ゲージの中の数字は、28の太字ぐらいの大きさにしておくと、ゲームの画面を少し離れて見ても読めます。小さい文字はウインドの中で埋もれがちなので、思い切って大きめにしましょう。

HpGaugeが出来たら、Ctrl+Dで複製して「MpGauge」にし、FillのColorを#3C7BD9(青)、Labelを「MP」に変えます。

Gaugeのスクリプト

  • TurnBasedRpg/Scriptsに「Gauge」スクリプトを作る
using TMPro;
using UnityEngine;
using UnityEngine.UI;

// ゲージの長さと「38/38」の数字を、今の値と最大値から決める
public class Gauge : MonoBehaviour
{
    [SerializeField] private Image fill;         // Image Type:Filled
    [SerializeField] private TMP_Text valueText; // 「38/38」

    private int current;
    private int max;

    public void SetValue(int newCurrent, int newMax)
    {
        current = newCurrent;
        max = newMax;
        fill.fillAmount = max > 0 ? (float)current / max : 0f;
        valueText.text = $"{current}/{max}";
    }

    // 確認用:Inspector の右上の︙から、ゲージを半分にしてみる
    [ContextMenu("半分にしてみる")]
    private void TestHalf()
    {
        SetValue(current / 2, max);
    }
}
  • fill.fillAmount
    • Filledの「どこまで塗るか」(0〜1)。HPが38のうち19なら0.5で、ゲージが半分になる
  • (float)current / max
    • intどうしの割り算は、小数が切り捨てられて0か1にしかならない。片方をfloatにしてから割るのがポイント
  • max > 0 ? … : 0f
    • MPが0のキャラクター(敵など)で、0で割らないようにしている
  • [ContextMenu(“…”)]
    • Inspectorのコンポーネントの︙(右上のメニュー)に、そのメソッドを呼ぶ項目を足す。再生中に、ゲージの動きを手で確かめられる

HpGauge・MpGaugeにGaugeを付けて、FillとValueをそれぞれドラッグします。

スライム

︙のメニューから呼べるの、便利ですね・・・。確かめるためだけのボタンを作らなくて済みます

StatusRowとStatusWindow

StatusRow

  • 「StatusRow」スクリプトを作る
using TMPro;
using UnityEngine;

// ステータスのウインドの1人分の行
public class StatusRow : MonoBehaviour
{
    [SerializeField] private TMP_Text nameText;
    [SerializeField] private TMP_Text levelText;
    [SerializeField] private Gauge hpGauge;
    [SerializeField] private Gauge mpGauge;

    public void SetName(string characterName)
    {
        nameText.text = characterName;
    }

    public void SetLevel(int level)
    {
        levelText.text = $"Lv{level}";
    }

    public void SetHp(int hp, int maxHp)
    {
        hpGauge.SetValue(hp, maxHp);
    }

    public void SetMp(int mp, int maxMp)
    {
        mpGauge.SetValue(mp, maxMp);
    }
}

行の中の部品をStatusRowの中に隠しておいて、外からはSetHpのようなメソッドだけで変えられるようにしています。ほかのスクリプトは、行の中がどう組まれているかを知らなくていいので、あとでゲージの形を変えても、呼ぶ側は直さずに済みます。

  • StatusRowにStatusRowスクリプトを付けて、NameText・LevelText・HpGauge・MpGaugeをドラッグ
  • StatusRowをTurnBasedRpg/Prefabsにドラッグしてプレハブにし、シーンのStatusRowは消す

シーンのStatusRowを残したままにすると、再生したときに、作られた3行の上にもう1行(中身が「スライム」のままの行)が並んでしまいます。プレハブにしたら、シーンの方は消しておきましょう。

StatusRowのプレハブの編集画面。HierarchyにStatusRow>NameText・LevelText・HpGauge(Back・Fill・Label・Value)・MpGauge(Back・Fill・Label・Value)、InspectorにHorizontal Layout GroupとStatus Row(4つの欄が埋まっている)

StatusWindow

3人分の行は、StatusWindowの中に縦に並べます。

  • StatusWindowにAdd Component>Vertical Layout Group
    • Padding:Left・Right・Top・Bottom 24、Spacing:8
    • Control Child Size:Width・Heightにチェック、Child Force Expand:Width・Heightにチェック
  • 「StatusWindow」スクリプトを作る
using UnityEngine;

// 右下のステータスのウインド。データから1人分ずつ行を作る
public class StatusWindow : MonoBehaviour
{
    [SerializeField] private StatusRow rowPrefab;

    // 仲間1人分の行を足して、Lv1・HPとMPが満タンの状態で表示する
    public StatusRow AddRow(CharacterData data)
    {
        StatusRow row = Instantiate(rowPrefab, transform);
        row.SetName(data.CharacterName);
        row.SetLevel(1);
        row.SetHp(data.BaseStats.maxHp, data.BaseStats.maxHp);
        row.SetMp(data.BaseStats.maxMp, data.BaseStats.maxMp);
        return row;
    }
}
  • Instantiate(rowPrefab, transform)
    • StatusWindow(自分)の子として行を作る。Vertical Layout Groupが付いているので、作るたびに自動で下に並んでいく
  • return row
    • 作った行を返しておくと、呼んだ側があとからHPを変えられる。#7でダメージを受けたときに使います

StatusWindowにStatusWindowスクリプトを付けて、Row PrefabにPrefabsのStatusRowをドラッグします。

StatusWindowのInspector。Vertical Layout GroupのSpacingが8、Control Child SizeとChild Force ExpandのWidth・Heightにチェック。Status WindowのRow PrefabにStatusRow

BattleLineupから行を作る

最後に、#2のBattleLineupで仲間を並べるときに、ステータスの行も一緒に作るようにします。

using UnityEngine;

// データから仲間と敵を並べる(バトルの流れは #9 で BattleManager に移す)
public class BattleLineup : MonoBehaviour
{
    [SerializeField] private CharacterData[] party;  // バトルに出る3人
    [SerializeField] private UnitView[] partyViews;  // PartyArea の3人
    [SerializeField] private EnemyData[] enemies;    // 出てくる敵(1〜4体)
    [SerializeField] private UnitView enemyPrefab;
    [SerializeField] private RectTransform enemyArea;
    [SerializeField] private float enemySpacing = 300f; // 敵の横の間隔
    [SerializeField] private StatusWindow statusWindow;

    private void Start()
    {
        for (int i = 0; i < partyViews.Length; i++)
        {
            CharacterData character = party[i];
            partyViews[i].SetupCharacter(character);
            statusWindow.AddRow(character);
        }

        for (int i = 0; i < enemies.Length; i++)
        {
            UnitView view = Instantiate(enemyPrefab, enemyArea);
            // 真ん中をそろえて横に並べ、1体おきに上下へずらす
            float x = (i - (enemies.Length - 1) / 2f) * enemySpacing;
            float y = (i % 2 == 0) ? 60f : -60f;
            ((RectTransform)view.transform).anchoredPosition = new Vector2(x, y);
            view.SetupEnemy(enemies[i]);
        }
    }
}
  • 変わったのは、statusWindowの欄と、仲間を並べるループの中だけ。データから見た目を作る流れに、行を作るのを1つ足しただけです
  • 同じparty[i]を2回使うので、CharacterData character = party[i];で変数に入れてから使います。何度もparty[i]と書くより読みやすく、番号の書きまちがいも防げます
  • BattleのBattle LineupのStatus Windowに、StatusWindowをドラッグ

仲間の数は、ロビーで入れ替えても3人のまま変わりませんが、データから行を作る形にしておくと、どの3人が出てきても同じスクリプトで表示できます。#20の入れ替えでも、このまま使えます。

動かして確認!

再生すると、右下に3人分の行が並びます。

右下のウインドに3人分の行が並んでいる

ゲージが動くかも確かめてみましょう。

  • 再生中に、Hierarchyで1行目のStatusRow(Clone)>HpGaugeを選ぶ
  • InspectorのGaugeの右上の︙>半分にしてみるを何回か押す
再生中、1行目のHpGaugeで「半分にしてみる」を2回押したあと。スライムちゃんのHPゲージが4分の1ぐらいに縮み、縮んだところに灰色がかった紺のBackが見えて、数字が「9/38」になっている

#3の動作確認

  • 右下のウインドに、3人の名前・Lv・HP・MPが1行ずつ並んでいる
  • HPのゲージは緑、MPのゲージは青で、中の数字が読みやすい大きさ
  • 「半分にしてみる」で、ゲージが左から縮み、数字も変わる
  • BattleのPartyを入れ替えると、ウインドの名前と数字も変わる
スライム

ゲージの端が、ちゃんと四角いままですね!

フェアリー

白い四角のおかげだね。#10では、ダメージを受けたときに減った分が少し遅れて縮む演出も足すから、楽しみにしててね

あとがき

今回は、仲間のHPとMPを表示するステータスのウインドを作りました。まだ数字は満タンのままですが、ゲージが並ぶと一気にRPGらしくなりますね。次回は、左下のウインドに「たたかう・まほう・どうぐ・ぼうぎょ・にげる」のコマンドを並べて、キーボードやゲームパッドでも選べるようにしたいと思います!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (1件)

コメントする

目次