【Unity入門】Statusコンポーネントを作って、GameObjectにHPを持たせよう
ゲームを作っていると、「プレイヤーにHPを持たせたい」「敵にも体力を持たせたい」という場面が必ず出てきます。
ゲームでいう「ステータス」とは、HPやMP、攻撃力など、そのキャラクターが持っている情報のことです。この記事では、その情報をまとめて持つ Status というコンポーネントを作ります。
Unityでは、こうした「性質」を コンポーネント として作り、GameObjectにアタッチすることで持たせます。この記事では、HPを持つ Status コンポーネントを作りながら、Unityらしい考え方を体験していきます。

1. Statusスクリプトを作る
Projectウィンドウで C# スクリプトを作成し、名前を Status にします。中身は次のとおりです。
using UnityEngine;
public class Status : MonoBehaviour
{
public int Hp = 100;
}
ポイントは3つです。
MonoBehaviourを継承しているので、GameObjectにアタッチできるpublicを付けたので、Inspectorに表示され、数値を変更できる= 100は初期値
ファイル名とクラス名は必ず同じにしてください。違うとアタッチできません。
2. Playerにアタッチする
- Hierarchyで
PlayerというGameObjectを用意する(2Dスプライトでも、3Dの立方体でもかまいません) - 作成した
Statusスクリプトを、Inspectorにドラッグ&ドロップする - Inspectorに
Statusコンポーネントが追加され、Hpの欄に100と表示される
これで、PlayerはHPを持つGameObjectになりました。Inspectorで数値を書き換えれば、ゲームを動かさなくてもHPを調整できます。
3. 「Statusを付けるとHPを持てる」のがコンポーネント
このとき、Playerの中身はこうなっています。
Player
├ Transform
├ SpriteRenderer
├ Rigidbody2D
└ Status
└ Hp = 100
ここで大切なのは、HPをPlayerクラスそのものに埋め込んでいない ことです。
- 見た目を出すのは
SpriteRenderer - 物理的な動きは
Rigidbody2D - HPなどの数値は
Status
というように、役割ごとに部品(コンポーネント)を組み合わせてGameObjectを作ります。「あの部品を付ければ、あの機能が使える」という考え方が、Unityの基本です。実際にどんな部品が付いているかは、GameObjectには何が付いている? GetComponentsでComponent一覧を表示してみよう【Unity】で確認できます。
4. Statusを増やしてみよう
HPだけでなく、攻撃力や防御力も持たせてみましょう。
using UnityEngine;
public class Status : MonoBehaviour
{
public int Hp = 100;
public int Attack = 10;
public int Defense = 5;
}

こうしておくと、PlayerにもEnemyにも同じ Status を付けるだけで、同じ仕組みを持たせられます。
| GameObject | Hp | Attack | Defense |
|---|---|---|---|
| Player | 100 | 10 | 5 |
| Enemy | 30 | 8 | 2 |
スクリプトは1つだけ。数値はGameObjectごとにInspectorで変えられます。「クラスを増やさなくても、部品の組み合わせと設定値でキャラクターを作り分けられる」ことが体感できます。
5. スクリプトからStatusを追加する(AddComponent)
ここまではInspectorで操作しましたが、スクリプトから同じことをする方法もあります。
using UnityEngine;
public class PlayerCreator : MonoBehaviour
{
void Start()
{
GameObject obj = new GameObject("Player");
Status status = obj.AddComponent<Status>();
status.Hp = 100;
Debug.Log(obj.name + "のHP:" + status.Hp);
}
}
new GameObject("Player")で、空のGameObjectを作るAddComponent<Status>()で、Statusコンポーネントを追加する。戻り値は追加されたStatusなので、変数に受け取れるstatus.Hp = 100;で、外からHPの値を設定する
Inspectorでドラッグ&ドロップしていた操作が、「AddComponent を呼ぶ」というコードに対応していると分かります。new GameObject() からComponentを組み合わせる流れは、new GameObject()から作ってみよう ― UnityのComponentはどう組み合わさるのかで詳しく解説しています。
「new Status()」と書きたくなったら
これまでのC#では、クラスは new でオブジェクトを作りました。そのため、Status も new Status() と書きたくなるかもしれません。普通のクラスと比べてみましょう。
// 普通のC#クラスなら
StatusData data = new StatusData();
data.Hp = 100;
// MonoBehaviourなら(GameObjectに付けて存在させる)
Status status = gameObject.AddComponent<Status>();
status.Hp = 100;
「普通のクラスは new、MonoBehaviourは AddComponent」と覚えてください。MonoBehaviourは、GameObjectに付けて初めて存在するComponentです。
MonoBehaviourを new で作ろうとすると、Unityが警告を出します。作られたStatusはどのGameObjectにも付いておらず、Start や Update も呼ばれません。
6. publicのままで大丈夫?
public int Hp は手軽ですが、実は どのスクリプトからでも自由に書き換えられる という弱点があります。
// どこかのスクリプトで、うっかり書いてしまった例
status.Hp = -9999;
HPがマイナスになったり、意図せず全回復したりするバグの原因になります。そこで、次のように書き換えます。
using UnityEngine;
public class Status : MonoBehaviour
{
[SerializeField]
private int hp = 100;
public int Hp => hp;
public void TakeDamage(int damage)
{
hp -= damage;
if (hp < 0)
{
hp = 0;
}
}
}
[SerializeField]を付けると、privateでもInspectorに表示され、数値を設定できるprivateなので、外のスクリプトから直接書き換えられないpublic int Hp => hp;は「読み取り専用」の窓口。外からはHPを読めるが、書き換えはできない- HPを減らすときは
TakeDamageを呼ぶ。0未満にならないよう、Status自身が守る
使う側は次のようになります。
Status status = GetComponent<Status>();
status.TakeDamage(20);
Debug.Log(status.Hp); // 80
注意点として、private にしたあとは、5章の status.Hp = 100; のような書き方はできなくなります(読み取り専用のため、エラーになります)。初期値を変えたいときは、Inspectorで設定するか、必要に応じて設定用のメソッドを用意します。publicとprivateの考え方(カプセル化)は、C#のカプセル化とは?Studentクラスのpublicをprivateに変えて理解しようで、イラスト付きの解説は【イラストでわかる】C#のカプセル化を初心者向けに徹底解説で確認できます。
7. まとめ
Statusのような自作コンポーネントを作り、アタッチすることで、GameObjectに性質を持たせられる- まずは
public int Hpで、「Inspectorに出して数値を変えられる」ことを体験する - 同じ
StatusをPlayerにもEnemyにも付けて使い回せる AddComponent<Status>()で、スクリプトからもコンポーネントを追加できる- 慣れてきたら
[SerializeField] privateとTakeDamageのようなメソッドで、安全に値を守る
「何でも1つのクラスに詰め込む」のではなく、「部品を組み合わせる」という感覚を、ぜひ身につけてください。
あわせて読みたい
- GameObjectには何が付いている? GetComponentsでComponent一覧を表示してみよう【Unity】
- new GameObject()から作ってみよう ― UnityのComponentはどう組み合わさるのか
- C#のカプセル化とは?Studentクラスのpublicをprivateに変えて理解しよう
- 勇者を育てて学ぶC#オブジェクト指向入門
練習問題
StatusにMpを追加し、Inspectorに表示されることを確認してみましょう- Enemyを作り、
Hp = 30のStatusを付けてみましょう Statusに回復用のHeal(int amount)メソッドを追加してみましょう。ただし、最大HPを超えないようにしてください(ヒント:maxHpを別に用意します)- キーを押したらPlayerの
TakeDamage(10)が呼ばれるスクリプトを書き、ConsoleでHPの減り方を確認しましょう
解答例
まず自分で考えて、コードを書いてから開いてみてください。書き方は1つではありません。動いていれば、違う書き方でも正解です。
問題1の解答例(Mpを追加する)
4章までの public 版の Status に、1行足すだけです。
using UnityEngine;
public class Status : MonoBehaviour
{
public int Hp = 100;
public int Mp = 50;
public int Attack = 10;
public int Defense = 5;
}
保存してUnityに戻ると、Inspectorの Status に Mp の欄が増えています。
問題2の解答例(EnemyにHp = 30のStatusを付ける)
新しいスクリプトは作りません。Playerと同じ Status を使い回します。
- Hierarchyで右クリックし、新しいGameObjectを作って、名前を
Enemyにする Statusスクリプトを、EnemyのInspectorにドラッグ&ドロップするStatusのHpを30に書き換える
PlayerとEnemyで、同じスクリプトなのに値が違うことを確認してください。
問題3の解答例(Healメソッドを追加する)
6章の private 版の Status に、maxHp と Heal を足します。
using UnityEngine;
public class Status : MonoBehaviour
{
[SerializeField]
private int maxHp = 100;
[SerializeField]
private int hp = 100;
public int Hp => hp;
public void TakeDamage(int damage)
{
hp -= damage;
if (hp < 0)
{
hp = 0;
}
}
public void Heal(int amount)
{
hp += amount;
if (hp > maxHp)
{
hp = maxHp;
}
}
}
回復したあとにHPが最大HPを超えていたら、最大HPに戻します。TakeDamage で0未満にならないようにしたのと、同じ考え方です。
1行で書くなら、hp = Mathf.Min(hp + amount, maxHp); でも同じ結果になります。
問題4の解答例(キーを押したらダメージを受ける)
Playerに Status と、次の DamageTest スクリプトを付けます。キー入力には、このシリーズで使っているInput Systemを使います。
using UnityEngine;
using UnityEngine.InputSystem;
public class DamageTest : MonoBehaviour
{
private Status status;
void Start()
{
status = GetComponent<Status>();
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
status.TakeDamage(10);
Debug.Log("HP:" + status.Hp);
}
}
}
ゲームを再生してスペースキーを押すと、Consoleに HP:90、HP:80 と表示されます。HPが0より下がらないことも確かめてみましょう。
注意:古い Input.GetKeyDown は、Input Systemだけを有効にしたプロジェクトではエラーになります。エラーが出たら、Project Settingsの Player にある「Active Input Handling」の設定を確認してください。











ディスカッション
コメント一覧
まだ、コメントがありません