Unityでよく使われる「デザインパターン」とは?

広告

Unityでゲームを作っていると、

  • 「同じようなコードを書く」
  • 「似た構造のゲームが多い」
  • 「整理された作り方がある」

と感じることがあります。

これは、多くのゲーム開発者が長年の経験から、

「こう作ると管理しやすい」

という“定番の設計方法”を使っているためです。

このような設計の考え方を、

デザインパターン

と呼びます。


デザインパターンは「答え」ではない

まず大事なのは、

「絶対にこの形で作らなければならない」

というものではありません。

むしろ、

  • 問題を整理しやすくする
  • コードを読みやすくする
  • チーム開発しやすくする
  • バグを減らす

ための「経験則」に近いものです。


Unityで特によく使われるパターン

Unityでは、ゲーム開発特有の事情から、
特に使われやすいパターンがあります。


Component(コンポーネント)

まず最重要です。

実はUnityそのものが、
この考え方で作られています。


GameObjectに部品を追加する

Unityでは:

Player
 ├ Transform
 ├ Rigidbody
 ├ Animator
 ├ AudioSource
 ├ PlayerMove
 ├ PlayerAttack

のように、

「機能を部品として追加」

しています。


昔ながらの巨大クラスとの違い

昔の設計では:

PlayerCharacter

という巨大クラスに、
全部を書き込むことがありました。

しかしUnityでは、

  • 移動
  • 攻撃
  • アニメ
  • HP管理

を分けて作ります。


Unityは「組み合わせ」を重視

これは非常に重要です。

Unityでは、

継承より組み合わせ

が基本思想です。


なぜ重要?

例えば:

飛べる敵
撃つ敵
追いかける敵

を作る場合、

部品化されていれば:

FlyComponent
ShootComponent
ChaseComponent

を組み合わせるだけで作れます。


Singleton(シングルトン)

ゲーム中に:

「1つだけ存在するもの」

を管理する方法です。


よくある例

  • GameManager
  • AudioManager
  • SaveManager
  • BGMManager

など。


using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;

        DontDestroyOnLoad(gameObject);
    }
}

これは何をしている?

このコードは:

同じGameManagerを複数作らせない

ようにしています。


Unityでは特によく使われる理由

Unityはシーン切り替えがあるため:

DontDestroyOnLoad(gameObject);

を使って、

シーン変更後も残したい管理オブジェクト

を作ることが多いです。


ただし注意

便利ですが乱用すると:

  • どこからでも触れる
  • 修正の影響が広がる
  • テストしにくい

という問題も起きます。


Observer(オブザーバー)

これは:

「変化を通知する」

仕組みです。

Unityではかなり重要です。


例えばHP変更

プレイヤーHPが変化したら:

  • UIを更新
  • ダメージ演出
  • サウンド再生

などをしたい場合があります。


using System;
using UnityEngine;

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

    private int hp = 100;

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

        OnHpChanged?.Invoke(hp);
    }
}

UI側

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

なぜ重要?

直接:

Player → UI
Player → Audio
Player → Effect

とつなぐと、
どんどん複雑になります。

Observerを使うと:

「通知だけする」

構造になります。

これを:

疎結合(そけつごう)

と言います。


State(ステート)

ゲームでは:

状態によって動作が変わる

ことが非常に多いです。


プレイヤー:

  • 待機
  • 移動
  • 攻撃
  • ダメージ
  • 死亡

敵AI:

  • 巡回
  • 発見
  • 追跡
  • 攻撃

シンプル例

public enum PlayerState
{
    Idle,
    Move,
    Attack
}

なぜ必要?

例えば:

攻撃中に移動できない
死亡中は攻撃できない

などを整理しやすくなります。


Factory(ファクトリー)

これは:

「生成処理をまとめる」

考え方です。


Unityでは生成が多い

Unityでは:

Instantiate()

を大量に使います。

例えば:

  • アイテム
  • エフェクト

など。


public class EnemyFactory : MonoBehaviour
{
    [SerializeField]
    private GameObject enemyPrefab;

    public GameObject Create(Vector3 pos)
    {
        return Instantiate(enemyPrefab, pos, Quaternion.identity);
    }
}

Object Pool(オブジェクトプール)

Unityで非常に重要です。


なぜ必要?

Instantiate()
Destroy()

を大量に行うと、
ゲームが重くなることがあります。


解決方法

作り直すのではなく:

「再利用」

します。


イメージ

生成
↓
使う
↓
非表示
↓
再利用

よく使われるもの

  • 爆発エフェクト
  • ダメージ数字

Strategy(ストラテジー)

これは:

処理を差し替える

考え方です。


武器:

  • 魔法

敵AI:

  • 突撃型
  • 遠距離型
  • 回復型

共通化

public interface IAttackStrategy
{
    void Attack();
}

メリット

新しい攻撃方法を追加しても:

既存コードを壊しにくい

です。


Unity独特の設計

Unityには独自文化もあります。


ScriptableObject活用

これはUnityで非常によく使われます。


データを分離

例えば:

武器性能
敵ステータス
アイテム情報

を:

ScriptableObject

として管理します。


メリット

  • Inspector編集可能
  • データ共有可能
  • データ管理しやすい

まとめ

Unityでは:

「継承より組み合わせ」

が非常に重要です。

特に重要なのは:

パターン用途
Component機能を部品化
Singleton1つだけ管理
Observer通知
State状態管理
Factory生成管理
Object Pool高速化
Strategy処理差し替え

です。


最後に

デザインパターンは:

「難しい理論」

ではありません。

むしろ:

「ゲームを整理して作るための知恵」

です。

Unityを学んでいると、
自然に少しずつ触れることになります。

まずは:

  • 「こういう考え方がある」
  • 「よく使われる形がある」

と知るだけでも十分です。

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

広告