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 | 機能を部品化 |
| Singleton | 1つだけ管理 |
| Observer | 通知 |
| State | 状態管理 |
| Factory | 生成管理 |
| Object Pool | 高速化 |
| Strategy | 処理差し替え |
です。
最後に
デザインパターンは:
「難しい理論」
ではありません。
むしろ:
「ゲームを整理して作るための知恵」
です。
Unityを学んでいると、
自然に少しずつ触れることになります。
まずは:
- 「こういう考え方がある」
- 「よく使われる形がある」
と知るだけでも十分です。




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