Unityで学ぶObserver(オブザーバー)パターン入門

広告

HP変更通知システムを作ってみよう

今回はUnityで:

  • HPが減る
  • UIが更新される

という仕組みを作りながら、

Observer(オブザーバー)パターン

を学びます。

この仕組みはUnityで非常によく使われます。

特に:

  • HPバー
  • スコア表示
  • ダメージ演出
  • サウンド再生
  • クエスト更新

などで重要になります。

今回は:

「変化を通知する」

という考え方を、
実際にUnityで手を動かしながら理解していきます。


今回作るもの

スペースキーを押すと:

  • プレイヤーHPが減る
  • UI表示が更新される

というシステムです。


完成イメージ

スペースキー押下
↓
Player の HP が減る
↓
OnHpChanged 通知
↓
UI が更新される

新規Unityプロジェクトを作成

Unity Hubから:

3D Core

で新規作成してください。

プロジェクト名は:

ObserverSample

などでOKです。


シーン準備

Hierarchyで右クリック:

Create Empty

を押します。

名前を:

Player

に変更します。


UI作成

Hierarchyで右クリック:

UI → Text – TextMeshPro

を選択します。

初回は:

TMP Essential Resources Import

が表示されるので:

Import

を押してください。


UIの名前変更

作成されたTextを:

HpText

に変更します。


文字サイズ変更

Inspectorで:

項目
Font Size40
TextHP : 100

くらいに変更してください。


Playerスクリプト作成

Projectビューで:

Scriptsフォルダ

を作成してください。

その中で:

Create → C# Script

を押します。

名前:

Player

Player.cs を編集

ダブルクリックしてVisual Studioを開きます。

中身を全部消して、
以下を書いてください。

Player.cs

using System;
using UnityEngine;

public class Player : MonoBehaviour
{
    public event Action<int> OnHpChanged;

    private int hp = 100;

    private void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            Damage(10);
        }
    }

    public void Damage(int damage)
    {
        hp -= damage;

        Debug.Log($"現在HP : {hp}");

        OnHpChanged?.Invoke(hp);
    }
}

スクリプトをPlayerへ追加

Unityへ戻ります。

Hierarchyの:

Player

を選択。

Inspectorへ:

Player.cs

をドラッグしてください。


動作確認

Playを押します。

スペースキーを押すと:

現在HP : 90
現在HP : 80

などがConsoleに表示されます。

まだUIは更新されません。


次はUI更新

ここから:

Observerパターン

が登場します。


HpUIスクリプト作成

Scriptsフォルダで:

Create → C# Script

名前:

HpUI

HpUI.cs

using UnityEngine;
using TMPro;

public class HpUI : MonoBehaviour
{
    [SerializeField]
    private Player player;

    [SerializeField]
    private TextMeshProUGUI hpText;

    private void Start()
    {
        player.OnHpChanged += UpdateHpText;

        UpdateHpText(100);
    }

    private void UpdateHpText(int hp)
    {
        hpText.text = $"HP : {hp}";
    }
}

HpUIを設定

Hierarchyの:

HpText

を選択。

Inspectorへ:

HpUI.cs

をドラッグ。


参照設定

Inspectorに:

項目設定
PlayerPlayerオブジェクト
Hp TextHpText

をドラッグして設定します。


再生してみよう

Playを押してください。

スペースキーを押すと:

HP : 90
HP : 80

と画面表示が更新されます。


ここが重要

Player側を見てください。

OnHpChanged?.Invoke(hp);

しか書いていません。

つまり:

「HP変わったよ」

と通知しているだけです。


PlayerはUIを知らない

Playerには:

UI更新
Text変更
画面表示

などのコードがありません。

つまり:

Player → UI

が直接つながっていません。


これを疎結合という

このように:

「相手を直接知らない」

設計を:

疎結合(そけつごう)

と言います。

Unityでは非常に重要です。


Observerパターンの流れ

Player
  ↓
HP変更通知
  ↓
UI更新

さらに追加してみよう

例えば:

ダメージエフェクト

も追加できます。


DamageEffect.cs 作成

using UnityEngine;

public class DamageEffect : MonoBehaviour
{
    [SerializeField]
    private Player player;

    private void Start()
    {
        player.OnHpChanged += PlayEffect;
    }

    private void PlayEffect(int hp)
    {
        Debug.Log("ダメージ演出!");
    }
}

空オブジェクト作成

Hierarchyで:

Create Empty

名前:

EffectManager

スクリプト追加

DamageEffect.cs

を追加。

Player欄へ:

Player

をドラッグ。


再生してみよう

スペースキーを押すと:

現在HP : 90
ダメージ演出!

が表示されます。


ここがObserverの強み

Playerは:

UI
Effect
Audio

を知りません。

それでも:

  • UI更新
  • 演出
  • サウンド

を追加できます。


もし直接つないでいたら?

Playerに:

ui.Update();
effect.Play();
audio.Play();

を書き続ける必要があります。

これが増えると:

  • 修正が大変
  • バグが増える
  • 再利用しづらい

になります。


Observerパターンは万能ではない

ここで重要なのが:

「便利だから全部event」

にしないことです。

実際の開発では:

Observerの乱用は危険

とも言われます。


なぜ危険?

例えば:

OnHpChanged

に:

  • UI
  • Audio
  • Effect
  • Quest
  • Analytics
  • Save
  • Achievement

など、
大量に登録されるとします。

すると:

HP変更
↓
大量の処理が動く

状態になります。


問題1

どこで何が動くかわからない

例えば:

OnHpChanged?.Invoke(hp);

だけなのに:

  • 音が鳴る
  • 演出が出る
  • セーブされる
  • 実績解除される

など、
裏で大量に動作する。

すると:

「影響範囲が見えない」

状態になります。


問題2

デバッグが難しい

例えば:

HP変更時にゲームが止まる

問題が起きたとします。

でも:

  • UI?
  • Audio?
  • Save?
  • Effect?

どこが原因かわかりにくい。

Observerは:

「見えない接続」

だからです。


問題3

登録解除忘れ

Unityでは非常によくあります。

例えば:

player.OnHpChanged += UpdateHpText;

したのに、

-=

を忘れる。

すると:

  • MissingReferenceException
  • NullReferenceException

の原因になります。


登録解除の例

private void OnDestroy()
{
    player.OnHpChanged -= UpdateHpText;
}

実務ではかなり重要です。


問題4

イベント地獄

大規模になると:

OnDead
OnDamage
OnItemAdded
OnLevelUp
OnQuest
OnSceneLoaded

など、
イベントだらけになります。

すると:

「流れを追うだけで大変」

になります。


Observerを使うべき場面

Observerが向いているのは:

「複数が反応する可能性がある」

場合です。

例えば:

イベントObserver向き
HP変更
スコア更新
アイテム取得
クエスト更新

向いていない場合

例えば:

Player → Weapon

のように:

1対1で強く関係する

なら、
普通のメソッド呼び出しの方が
わかりやすい場合もあります。


Unityでありがちな失敗

初心者でよくあるのが:

「全部event化」

です。

ですが設計では:

「疎結合 = 正義」

ではありません。


最も重要なのは?

設計で本当に重要なのは:

「あとから読んで理解しやすい」

ことです。


UnityでObserverが重要な理由

それでもObserverは、
Unityでは非常によく使われます。

例えば:

システム通知内容
HPHP変更
スコアスコア更新
クエスト達成通知
UI値変更
Audioイベント発生

などです。


今回学んだ重要キーワード

用語意味
event通知
Action登録できるメソッド型
Invoke呼び出し
+=登録
-=登録解除
Observer変化監視
疎結合相手を直接知らない

まとめ

Observerパターンは:

「変化を通知する」

設計です。

Unityでは:

  • UI更新
  • サウンド
  • エフェクト
  • スコア管理

など、
非常に多くの場所で使われています。

特に重要なのは:

「通知だけする」

という考え方です。

ただし乱用すると:

  • 影響範囲不明
  • デバッグ困難
  • イベント地獄

になることもあります。

重要なのは:

「本当に通知が必要か?」

を考えることです。

Observerは:

「便利だから使う」

ではなく、

「複数へ変化を知らせたいから使う」

という意識が大切になります。

訪問数 1 回, 今日の訪問数 1回

広告