UnityUnityメモ

Unityのスクリプト設計を改善!SOLID原則で整理する方法

Unity
  1. 1. はじめに
    1. SOLID原則を適用するとコードがどう整理されるのか
    2. 記事の概要(初心者向けにわかりやすく解説)
  2. 2. SOLID原則とは?
    1. オブジェクト指向設計の基本
    2. SOLIDの5つの原則の概要
    3. S:単一責任の原則(Single Responsibility Principle)
    4. O:オープン・クローズドの原則(Open/Closed Principle)
    5. L:リスコフの置換原則(Liskov Substitution Principle)
    6. I:インターフェース分離の原則(Interface Segregation Principle)
    7. D:依存関係逆転の原則(Dependency Inversion Principle)
  3. 3. UnityスクリプトにSOLID原則を適用する方法
    1. 単一責任の原則(SRP)
    2. オープン・クローズドの原則(OCP)
    3. リスコフの置換原則(LSP)
    4. インターフェース分離の原則(ISP)
    5. 依存関係逆転の原則(DIP)
    6. まとめ
  4. 4. SOLID原則を使ったUnityスクリプトの具体的な実装
    1. サンプルコードを用いた具体的な適用方法
    2. MonoBehaviourを継承する場合の注意点
    3. SOLID設計を意識したコードリファクタリングの手順
    4. まとめ
  5. 5. SOLID原則を適用したプロジェクト管理のコツ
    1. 1. スクリプトのフォルダ分け(役割ごとに整理)
    2. 2. 名前付けルールの統一
    3. 3. メンテナンスしやすいコード構造の作り方
    4. 4. コメントとドキュメントの活用
    5. 5. 依存関係を最小限にする設計
    6. まとめ
  6. 6. まとめ
    1. SOLID原則をUnityスクリプトに適用するメリット
    2. まずは単一責任の原則から意識するのがオススメ
    3. 実践的なコード整理でゲーム開発をスムーズに
  7. よくある質問(FAQ)
    1. 関連記事:

1. はじめに

Unityでゲームを作っていると、「コードがどんどん増えて、どこで何をしているのかわからなくなった……」という経験はありませんか?
特にゲーム開発では、キャラクターの動き、敵のAI、スコアの管理、UIの制御など、さまざまな要素をスクリプトで制御するため、気づけば長大なコードが一つのスクリプトに詰め込まれてしまうこともよくあります。

例えば、プレイヤーの移動を処理するスクリプトに、攻撃やアイテムの取得、ダメージ処理まで全部詰め込んでしまうと、後から修正や機能追加をするたびに影響範囲が広がり、バグを生みやすくなります。
こうした問題を避けるには、**「スクリプトの整理」**がとても重要です。

SOLID原則を適用するとコードがどう整理されるのか

そこで役立つのが 「SOLID原則」 というオブジェクト指向プログラミングの考え方です。
SOLID原則とは、スクリプトを整理し、管理しやすくするための5つのルール であり、これを意識することで以下のようなメリットがあります。

  • スクリプトがシンプルになる → 一つのスクリプトが一つの役割を持つため、どこで何をしているのかが明確になる
  • 変更がしやすくなる → 影響範囲が小さくなるため、修正や機能追加が簡単になる
  • バグが減る → 一つのスクリプトに多くの処理を書かないので、意図しないバグを防ぎやすくなる

つまり、SOLID原則を意識することで、「わかりやすく、管理しやすいスクリプト」 を書くことができるのです。

記事の概要(初心者向けにわかりやすく解説)

この記事では、Unityのスクリプトを整理するために役立つ SOLID原則の基本と、実際のUnityスクリプトでの適用方法 をわかりやすく解説していきます。

  • SOLID原則とは何か?
  • Unityのスクリプトにどう適用すればいいのか?
  • 具体的なコード例を使った解説

これらを順番に説明しながら、今すぐ使える実践的なスクリプト整理術 を紹介していきます!
Unityのコードをスッキリ整理して、スムーズなゲーム開発を目指しましょう!




2. SOLID原則とは?

オブジェクト指向設計の基本

ゲーム開発では、多くのオブジェクト(キャラクター、敵、アイテム、UI など)が登場し、それぞれがさまざまな処理を行います。そのため、オブジェクトごとの役割を明確にし、拡張しやすく、バグを少なくするための**「設計のルール」**が重要になります。

そこで登場するのが**「オブジェクト指向設計」**です。オブジェクト指向では、スクリプトを「役割ごとの部品」として作り、それらを組み合わせてゲームを構築します。適切に設計することで、コードの管理がしやすくなり、変更や追加が簡単になります。

しかし、ただ「オブジェクトを分ける」だけでは、適切な設計にならないこともあります。
そこで役立つのが、SOLID原則 という5つのルールです。


SOLIDの5つの原則の概要

SOLID原則とは、「オブジェクト指向設計を正しく行うためのルール」 であり、以下の5つの原則から成り立っています。

  1. S(単一責任の原則 – Single Responsibility Principle)
  2. O(オープン・クローズドの原則 – Open/Closed Principle)
  3. L(リスコフの置換原則 – Liskov Substitution Principle)
  4. I(インターフェース分離の原則 – Interface Segregation Principle)
  5. D(依存関係逆転の原則 – Dependency Inversion Principle)

それぞれの原則について、Unityでの具体的な適用例を交えて説明していきます。


S:単一責任の原則(Single Responsibility Principle)

「1つのクラス(スクリプト)は、1つの責務(役割)のみを持つべき」

❌ 悪い例(NGな書き方)

public class Player : MonoBehaviour
{
public void Move() { /* 移動処理 */ }
public void Attack() { /* 攻撃処理 */ }
public void TakeDamage() { /* ダメージ処理 */ }
public void DisplayHealthUI() { /* HP表示 */ }
}

このように、「移動」「攻撃」「ダメージ処理」「UI表示」など、さまざまな責任を1つのクラスが持っていると、修正が大変になります。

✅ 良い例(単一責任に分割)

public class PlayerMovement : MonoBehaviour
{
public void Move() { /* 移動処理 */ }
}

public class PlayerCombat : MonoBehaviour
{
public void Attack() { /* 攻撃処理 */ }
}

public class PlayerHealth : MonoBehaviour
{
public void TakeDamage() { /* ダメージ処理 */ }
}

public class PlayerUI : MonoBehaviour
{
public void DisplayHealthUI() { /* HP表示 */ }
}

こうすることで、それぞれの役割が明確になり、修正や拡張がしやすくなります。


O:オープン・クローズドの原則(Open/Closed Principle)

「クラス(スクリプト)は拡張に対して開いていて、修正に対して閉じているべき」

これは、既存のコードを修正せずに、新しい機能を追加できるように設計するべき、という考え方です。

❌ 悪い例(直接クラスを修正する)

public class Enemy
{
public void Attack() { /* 通常攻撃 */ }
}

もし新しい「魔法攻撃」タイプの敵を作りたくなった場合、Enemy クラスを直接修正しなければなりません。

✅ 良い例(拡張可能な設計)

public abstract class Enemy : MonoBehaviour
{
public abstract void Attack();
}

public class MeleeEnemy : Enemy
{
public override void Attack() { /* 近接攻撃 */ }
}

public class MagicEnemy : Enemy
{
public override void Attack() { /* 魔法攻撃 */ }
}

このように、基盤となるクラス(Enemy)を変更せずに、新しいタイプの敵を追加 できます。


L:リスコフの置換原則(Liskov Substitution Principle)

「子クラス(サブクラス)は、親クラス(スーパークラス)として代用できるべき」

サブクラスが、親クラスの代わりとして問題なく動作するべき、という原則です。

❌ 悪い例(サブクラスが親クラスのルールを破る)

public class Bird
{
public virtual void Fly() { Debug.Log("鳥が飛ぶ"); }
}

public class Penguin : Bird
{
public override void Fly() { throw new System.Exception("ペンギンは飛べません!"); }
}

この場合、ペンギンは Bird を継承していますが、「飛べない」という例外を出してしまっています。これは良くない設計です。

✅ 良い例(適切な継承)

public abstract class Bird { }

public class FlyingBird : Bird
{
public void Fly() { Debug.Log("鳥が飛ぶ"); }
}

public class Penguin : Bird
{
public void Swim() { Debug.Log("ペンギンが泳ぐ"); }
}

このように、「飛べる鳥」と「泳ぐ鳥」を別々に定義 することで、問題を解決できます。




I:インターフェース分離の原則(Interface Segregation Principle)

「大きなインターフェースを、小さなインターフェースに分割するべき」

❌ 悪い例(1つのインターフェースに多すぎる機能)

public interface ICharacter
{
void Attack();
void CastMagic();
void Heal();
}

すべてのキャラクターが魔法を使うわけではないので、これは適切ではありません。

✅ 良い例(インターフェースを分割)

public interface IAttacker { void Attack(); }
public interface IMagicUser { void CastMagic(); }
public interface IHealer { void Heal(); }

これにより、必要な機能だけを実装できます。


D:依存関係逆転の原則(Dependency Inversion Principle)

「高レベルモジュールは、低レベルモジュールに直接依存せず、抽象に依存するべき」

❌ 悪い例(具体的なクラスに依存している)

public class Game
{
private Player player = new Player();
}

✅ 良い例(抽象化を利用)

public class Game
{
private ICharacter character;

public Game(ICharacter character)
{
this.character = character;
}
}

これにより、ICharacter を実装した異なるキャラクター(PlayerEnemy など)を簡単に変更できます。


「SOLID原則を適用すると、コードの可読性が上がり、管理しやすくなります。ただし、MonoBehaviourを継承したスクリプトのプロパティが増えてくると、インスペクターが見づらくなることも…。
そんなときに役立つのが Odin Inspector and Serializer です。
このアセットを使うと、スクリプトを整理しながら、見やすいカスタムインスペクターを簡単に作成できます!」

SOLID原則を意識することで、Unityのスクリプトを整理し、メンテナンスしやすいコードを作ることができます。次のセクションでは、具体的なUnityスクリプトにSOLID原則を適用する方法を詳しく解説していきます!




3. UnityスクリプトにSOLID原則を適用する方法

ここでは、UnityのスクリプトにSOLID原則をどのように適用できるのか、具体例を交えながら解説していきます。


単一責任の原則(SRP)

「1つのスクリプトは、1つの役割のみを持つようにする」

❌ 悪い例(1つのスクリプトに複数の役割を持たせる)

public class Player : MonoBehaviour
{
public void Move() { /* プレイヤーの移動処理 */ }
public void Attack() { /* 攻撃処理 */ }
public void TakeDamage() { /* ダメージ処理 */ }
}

このコードでは、「移動」「攻撃」「ダメージ処理」がすべて Player クラスに詰め込まれており、スクリプトが複雑になっています。

✅ 良い例(役割ごとにスクリプトを分割)

public class PlayerMovement : MonoBehaviour
{
public void Move() { /* プレイヤーの移動処理 */ }
}

public class PlayerCombat : MonoBehaviour
{
public void Attack() { /* 攻撃処理 */ }
}

public class PlayerHealth : MonoBehaviour
{
public void TakeDamage() { /* ダメージ処理 */ }
}

こうすることで、各スクリプトが1つの責任に集中し、コードの管理がしやすくなります。例えば、移動のロジックを変更する場合、PlayerMovement だけを編集すればよくなります。


オープン・クローズドの原則(OCP)

「既存のコードを変更せずに拡張できる設計にする」

例えば、敵の攻撃パターンを増やしたいとき、直接 Enemy クラスを変更すると、バグが発生しやすくなります。

❌ 悪い例(敵の攻撃タイプを直接 Enemy クラスに追加)

public class Enemy : MonoBehaviour
{
public void Attack(string type)
{
if (type == "Melee") { /* 近接攻撃 */ }
else if (type == "Ranged") { /* 遠距離攻撃 */ }
}
}

攻撃タイプが増えるたびに Enemy クラスを修正する必要があり、拡張しづらくなります。

✅ 良い例(拡張可能な設計 – 継承を活用)

public abstract class Enemy : MonoBehaviour
{
public abstract void Attack();
}

public class MeleeEnemy : Enemy
{
public override void Attack() { /* 近接攻撃処理 */ }
}

public class RangedEnemy : Enemy
{
public override void Attack() { /* 遠距離攻撃処理 */ }
}

こうすることで、新しい攻撃パターンを追加する場合、新しいクラスを作るだけで済みますEnemy クラスを直接変更する必要がないため、安全に拡張できます。




リスコフの置換原則(LSP)

「サブクラスはスーパークラスと完全に互換性を持つべき」

これは、親クラスの代わりとしてサブクラスが正常に動作するようにする というルールです。

❌ 悪い例(サブクラスが親クラスの想定を崩してしまう)

public class Bird
{
public virtual void Fly() { Debug.Log("鳥が飛ぶ"); }
}

public class Penguin : Bird
{
public override void Fly() { throw new System.Exception("ペンギンは飛べません!"); }
}

このコードでは、Bird を継承している Penguin ですが、「飛べない」ため、親クラスの振る舞いを壊してしまっています。

✅ 良い例(適切な継承関係)

public abstract class Bird { }

public class FlyingBird : Bird
{
public void Fly() { Debug.Log("鳥が飛ぶ"); }
}

public class Penguin : Bird
{
public void Swim() { Debug.Log("ペンギンが泳ぐ"); }
}

「飛ぶ鳥」と「泳ぐ鳥」を別々のクラスに分けることで、リスコフの置換原則を守ることができます」


インターフェース分離の原則(ISP)

「大きなインターフェースを、小さなインターフェースに分割する」

❌ 悪い例(1つのインターフェースに多すぎる機能)

public interface ICharacter
{
void Attack();
void CastMagic();
void Heal();
}

すべてのキャラクターが魔法を使うわけではないため、不適切な設計です。

✅ 良い例(インターフェースを分割)

public interface IAttacker { void Attack(); }
public interface IMagicUser { void CastMagic(); }
public interface IHealer { void Heal(); }

こうすることで、必要な機能だけを実装できるため、不要なメソッドを無理に実装する必要がなくなります


依存関係逆転の原則(DIP)

「依存関係を抽象化することで、柔軟な設計にする」

この原則では、高レベルモジュール(ゲーム全体の流れを制御する部分)が、低レベルモジュール(個々のクラス)に直接依存しないようにします。

❌ 悪い例(特定のクラスに直接依存している)

public class Game
{
private Player player = new Player();
}

この場合、Game クラスは Player クラスに強く依存しており、別のキャラクターに変更するのが難しくなります。

✅ 良い例(依存関係を抽象化する)

public class Game
{
private ICharacter character;

public Game(ICharacter character)
{
this.character = character;
}
}

こうすることで、ICharacter を実装した PlayerEnemy など、異なるキャラクターを簡単に変更できます


まとめ

SOLID原則をUnityのスクリプトに適用することで、整理しやすく、拡張しやすいコード を作ることができます。

単一責任の原則(SRP) → スクリプトを役割ごとに分割
オープン・クローズドの原則(OCP) → 既存のコードを変更せずに新機能を追加
リスコフの置換原則(LSP) → サブクラスが親クラスとして問題なく動作するようにする
インターフェース分離の原則(ISP) → インターフェースを小さく分割し、不要な実装を防ぐ
依存関係逆転の原則(DIP) → 抽象クラスやインターフェースを使って依存関係を柔軟にする

次のセクションでは、具体的なUnityプロジェクトでのコード適用例をさらに詳しく解説 していきます!




4. SOLID原則を使ったUnityスクリプトの具体的な実装

ここでは、SOLID原則をUnityスクリプトに適用する具体的な実装方法を紹介します。特に MonoBehaviour を継承する場合の注意点 や、既存のコードをSOLID設計にリファクタリングする手順 についても詳しく解説していきます。


サンプルコードを用いた具体的な適用方法

単一責任の原則(SRP)を適用したスクリプトの整理

改善前(1つのスクリプトに複数の責務がある)
public class Player : MonoBehaviour
{
public void Move() { /* 移動処理 */ }
public void Attack() { /* 攻撃処理 */ }
public void TakeDamage() { /* ダメージ処理 */ }
public void DisplayHealthUI() { /* HPをUIに表示 */ }
}

このように、移動、攻撃、ダメージ処理、UIの更新が1つのスクリプトにまとまってしまうと、修正や拡張が難しくなります。

改善後(責務ごとにスクリプトを分割)
public class PlayerMovement : MonoBehaviour
{
public void Move() { /* 移動処理 */ }
}

public class PlayerCombat : MonoBehaviour
{
public void Attack() { /* 攻撃処理 */ }
}

public class PlayerHealth : MonoBehaviour
{
public void TakeDamage() { /* ダメージ処理 */ }
}

public class PlayerUI : MonoBehaviour
{
public void DisplayHealthUI() { /* HP表示処理 */ }
}

こうすることで、各スクリプトが1つの役割に集中し、コードの管理がしやすくなります


オープン・クローズドの原則(OCP)を適用した拡張性のあるスクリプト

改善前(既存のクラスを直接変更しなければならない設計)
public class Enemy : MonoBehaviour
{
public void Attack(string type)
{
if (type == "Melee") { /* 近接攻撃処理 */ }
else if (type == "Ranged") { /* 遠距離攻撃処理 */ }
}
}

この場合、新しい攻撃タイプを追加するたびにEnemy クラスを編集しなければならず、コードが膨れ上がります

改善後(拡張可能な設計 – 継承を活用)
public abstract class Enemy : MonoBehaviour
{
public abstract void Attack();
}

public class MeleeEnemy : Enemy
{
public override void Attack() { /* 近接攻撃処理 */ }
}

public class RangedEnemy : Enemy
{
public override void Attack() { /* 遠距離攻撃処理 */ }
}

こうすることで、新しい攻撃パターンを追加する場合、新しいクラスを作るだけで済み、既存のEnemy クラスを変更する必要がなくなります


MonoBehaviourを継承する場合の注意点

Unityでは、多くのスクリプトが MonoBehaviour を継承しているため、継承の使いすぎに注意 する必要があります。

MonoBehaviourを継承するクラスを減らす工夫

MonoBehaviour を継承すると、GameObject にアタッチする必要がある ため、以下のように適切に分離するのが理想です。

悪い例(不要にMonoBehaviourを継承)
public class PlayerCombat : MonoBehaviour
{
public void Attack() { /* 攻撃処理 */ }
}

この場合、PlayerCombatMonoBehaviour を継承していますが、攻撃の処理自体には MonoBehaviour の機能は不要 です。

良い例(MonoBehaviourを継承せずに、他のクラスで利用する)
public class PlayerCombat
{
public void Attack() { /* 攻撃処理 */ }
}

public class Player : MonoBehaviour
{
private PlayerCombat combat = new PlayerCombat();

public void PerformAttack()
{
combat.Attack();
}
}

こうすることで、PlayerCombat を通常のC#クラスとして扱い、MonoBehaviourを継承するクラスを減らすことでスクリプトを整理できます




SOLID設計を意識したコードリファクタリングの手順

  1. 単一責任の原則(SRP)を意識して、1つのスクリプトが1つの役割を持つようにする
    • 移動、攻撃、UI管理などを分割する
    • 必要に応じて MonoBehaviour の継承を外す
  2. オープン・クローズドの原則(OCP)を適用し、新機能を追加しやすい設計にする
    • abstract クラスを利用し、既存のコードを変更せずに拡張できるようにする
    • 例: Enemy クラスを MeleeEnemyRangedEnemy に分ける
  3. リスコフの置換原則(LSP)を考慮し、サブクラスが親クラスとして違和感なく動作するか確認する
    • 例: FlyingBirdPenguinBird クラスから適切に分離する
  4. インターフェース分離の原則(ISP)を適用し、必要な機能だけを実装できるようにする
    • 例: ICharacter インターフェースを分割し、IAttackerIMagicUserIHealer などの細かいインターフェースにする
  5. 依存関係逆転の原則(DIP)を利用し、柔軟にコンポーネントを管理できるようにする
    • 依存関係を ICharacter のようなインターフェースにし、具体的なクラスに依存しないようにする
    • 例: Game クラスが Player ではなく、ICharacter を使うようにする

まとめ

SOLID原則を適用すると、スクリプトが整理され、修正・拡張がしやすくなる
MonoBehaviourを継承する場合は、本当に必要かどうか考える
既存のスクリプトをリファクタリングすることで、より良い設計にできる


「SOLID原則を意識すると、スクリプトの役割を分け、整理されたプロジェクト構造を作ることが重要です。
しかし、スクリプトが増えてくると、インスペクターの表示が乱雑になったり、オブジェクト管理が煩雑になることも…。
そこで便利なのが Odin Inspector and Serializer です。
カスタムインスペクターを活用することで、プロジェクトの管理が劇的に楽になります!」

次のセクションでは、SOLID原則を適用したプロジェクトの管理方法について 解説していきます!




5. SOLID原則を適用したプロジェクト管理のコツ

SOLID原則を理解し、スクリプトを適切に設計したとしても、プロジェクト全体の管理が不十分だと、スクリプトが増えるにつれて混乱しやすくなります。そこで、SOLID原則を適用したプロジェクト管理のコツを紹介します。


1. スクリプトのフォルダ分け(役割ごとに整理)

悪い例(スクリプトがバラバラ)

/Assets
├── Scripts
├── PlayerMovement.cs
├── Enemy.cs
├── Attack.cs
├── GameManager.cs
├── UIManager.cs
├── Health.cs

このように すべてのスクリプトが 1 つのフォルダにあると、どこに何があるのか分かりづらくなります

良い例(役割ごとに整理)

/Assets
├── Scripts
├── Player
│ ├── PlayerMovement.cs
│ ├── PlayerCombat.cs
│ ├── PlayerHealth.cs

├── Enemy
│ ├── BaseEnemy.cs
│ ├── MeleeEnemy.cs
│ ├── RangedEnemy.cs

├── Managers
│ ├── GameManager.cs
│ ├── UIManager.cs

├── UI
│ ├── HealthBar.cs
│ ├── ScoreDisplay.cs

このように フォルダを「役割ごと」に分けることで、スクリプトを探しやすくなります


2. 名前付けルールの統一

スクリプトの名前付けが統一されていないと、どんな役割のスクリプトなのか分かりにくくなります

悪い例(統一されていない命名)

public class MovePlayer { }  // 動詞が先で統一感がない
public class PlayerAttackScript { } // "Script" という余計な単語
public class Health { } // プレイヤー用か敵用か不明

良い例(統一された命名)

public class PlayerMovement { }  // "Player" という接頭辞で明確
public class PlayerCombat { } // 役割が明確
public class PlayerHealth { } // 何の "Health" か分かる

💡 ポイント

  • クラス名は 「役割 + 対象」 の形式で付ける
  • ManagerController のような 管理クラスは明確な名前にする
  • I を付けてインターフェースを区別する(例:IAttackable

3. メンテナンスしやすいコード構造の作り方

✅ コーディングスタイルの統一

コードの書き方が統一されていないと、可読性が下がります。
チーム開発でも個人開発でも、書き方を統一することでメンテナンスしやすくなります

❌ 悪い例(統一されていないコードスタイル)

public class Player {public void Move(){ Debug.Log("移動");}}

インデントがバラバラで、見づらい

✅ 良い例(統一されたコードスタイル)

public class Player
{
public void Move()
{
Debug.Log("移動");
}
}

ポイント

  • インデントを統一する(タブ or スペース)
  • メソッドの開始 { は次の行に書く
  • 変数名はキャメルケース(playerHealth)にする
  • クラス名はパスカルケース(PlayerHealth)にする



4. コメントとドキュメントの活用

SOLID原則を適用すると、スクリプトが整理されますが、コードを読んだだけで全員が理解できるとは限りません
適切に コメントやドキュメントを活用 しましょう。

✅ 良いコメントの例

/// <summary>
/// プレイヤーの移動を制御するクラス
/// </summary>
public class PlayerMovement : MonoBehaviour
{
/// <summary> プレイヤーの移動速度 </summary>
public float speed = 5f;

/// <summary> 移動処理 </summary>
public void Move()
{
// 入力を取得
float move = Input.GetAxis("Horizontal");
transform.Translate(Vector3.right * move * speed * Time.deltaTime);
}
}

💡 ポイント

  • /// <summary> を使ってクラスとメソッドの説明を記述
  • 処理が複雑な部分はコメントを残す
  • 不必要なコメントは書かない(コードを見れば分かるものは不要)

5. 依存関係を最小限にする設計

SOLID原則の 「依存関係逆転の原則(DIP)」 を意識すると、スクリプト間の結びつきを減らし、変更に強いコードになります。

❌ 悪い例(強く結びついた依存関係)

public class GameManager : MonoBehaviour
{
private Player player = new Player();
}

このように GameManagerPlayer に直接依存していると、変更しづらくなります

✅ 良い例(依存関係を減らす設計)

public class GameManager : MonoBehaviour
{
private ICharacter character;

public GameManager(ICharacter character)
{
this.character = character;
}
}

💡 ポイント

  • 具体的なクラス (PlayerEnemy) ではなく、抽象クラス (ICharacter) を利用
  • GameManager を直接 Player に依存させず、柔軟に変更できるようにする

まとめ

SOLID原則を適用するだけでなく、プロジェクト全体の管理を徹底することで、さらに開発がスムーズになります

スクリプトを役割ごとにフォルダ分けする → 探しやすく整理する
名前付けルールを統一する → 役割が分かりやすいクラス名にする
メンテナンスしやすいコードスタイルを採用する → インデントや変数命名を統一する
コメントとドキュメントを活用する → コードの意図を伝えやすくする
依存関係を最小限にする設計を心がける → 変更に強いコードにする

次のセクションでは、よくある質問(Q&A)形式で、SOLID原則の実践方法や疑問点を解説していきます!




6. まとめ

この記事では、SOLID原則をUnityのスクリプトに適用する方法について解説しました。SOLID原則を意識して設計することで、コードの整理ができ、メンテナンスや拡張がしやすくなります。


SOLID原則をUnityスクリプトに適用するメリット

スクリプトの役割が明確になり、管理しやすくなる

  • Player.cs に移動や攻撃、UI更新などをすべて詰め込むのではなく、「移動」「攻撃」「UI」などの役割ごとにスクリプトを分ける ことで、コードがスッキリ整理される。

バグが減り、修正がしやすくなる

  • Enemy.cs を直接修正するのではなく、BaseEnemy.cs を作成し、それを継承した MeleeEnemy.csRangedEnemy.cs を用意することで、新しい機能を追加しても既存のコードに影響を与えずに済む

開発スピードが向上する

  • しっかり設計されたコードは、機能追加がしやすく、修正やデバッグの時間を削減できる ため、開発のスピードが向上する。

まずは単一責任の原則から意識するのがオススメ

SOLID原則の中でも、特に**「単一責任の原則(SRP)」** を意識するだけで、コードがぐっと整理されます。

例えば、次のようなケースで「単一責任の原則」を適用できます。

悪い例(すべてを1つのスクリプトにまとめる)

public class Player : MonoBehaviour
{
public void Move() { /* 移動処理 */ }
public void Attack() { /* 攻撃処理 */ }
public void TakeDamage() { /* ダメージ処理 */ }
public void UpdateUI() { /* UI更新処理 */ }
}

このように 複数の責務を1つのスクリプトにまとめると、修正や管理が大変になります

良い例(単一責任の原則を適用)

public class PlayerMovement : MonoBehaviour
{
public void Move() { /* 移動処理 */ }
}

public class PlayerCombat : MonoBehaviour
{
public void Attack() { /* 攻撃処理 */ }
}

public class PlayerHealth : MonoBehaviour
{
public void TakeDamage() { /* ダメージ処理 */ }
}

public class PlayerUI : MonoBehaviour
{
public void UpdateUI() { /* UI更新処理 */ }
}

こうすることで、スクリプトの役割が明確になり、バグを減らしながら開発を進めやすくなります


実践的なコード整理でゲーム開発をスムーズに

SOLID原則を適用すると、スクリプトが整理され、プロジェクト全体の見通しが良くなります

💡 今すぐできる実践的なコード整理術

  1. スクリプトを役割ごとにフォルダ分けする
    • Scripts/Player/Scripts/Enemy/ のように、カテゴリごとにフォルダを作る
  2. クラス名を分かりやすく統一する
    • PlayerMovement / EnemyAI / HealthManager のように、明確な名前を付ける
  3. コメントやドキュメントを適切に記述する
    • /// <summary> を活用し、何をするクラスなのかを明確にする
  4. 不要な MonoBehaviour の継承を減らす
    • MonoBehaviour を継承するのは必要最小限にし、できるだけ通常の C# クラスを使う

SOLID原則を活用すれば、スクリプトの整理がしやすくなり、開発の効率が大幅に向上します!
最初からすべての原則を完璧に適用する必要はありませんが、まずは「単一責任の原則」から意識し、徐々に他の原則も取り入れてみるのがオススメです

整理されたコードで、スムーズなゲーム開発を楽しみましょう! 🎮✨




よくある質問(FAQ)

Q
Unityのスクリプトはどの原則を一番意識すべき?
A

「単一責任の原則(SRP)」を最優先で意識するのがおすすめです。

SOLID原則の中でも、特に 「単一責任の原則(SRP)」 を意識することで、コードの整理がしやすくなります。

例えば、1つのスクリプトに「移動」「攻撃」「HP管理」「UI更新」などの処理がすべて詰め込まれていると、修正や拡張が非常に大変になります。

単一責任の原則を意識すると…

  • スクリプトごとに役割が明確になり、コードを探しやすくなる
  • 不要な修正を減らし、バグを防ぎやすくなる
  • 新機能の追加が簡単になる

💡 まずは「1つのスクリプト = 1つの責務」から意識してみましょう!

Q
SOLID原則を意識するとコードが増えてしまうが、どうすればいい?
A

コードの量が増えるのは自然なことですが、それは「管理しやすいコード」になっている証拠です!

たしかに、SOLID原則を意識するとクラスの数は増えます。しかし、その分 「どこに何の処理があるのか」が明確になるため、長期的に見るとメリットが大きい です。

例えば、1つの巨大なスクリプトにすべての処理を書いてしまうと、

  • 修正のたびに影響範囲が広がる
  • バグが増えやすい
  • コードが読みにくくなる

といった問題が発生します。

💡 コードが増えても、管理しやすいなら問題なし!

  • フォルダ分けをして、スクリプトを整理する
  • 共通処理は Utility クラスや Manager クラスにまとめる
  • MonoBehaviourを継承するクラスを減らし、通常のC#クラスを活用する

これらを意識すれば、コードが増えても開発しやすい環境を作ることができます!

Q
MonoBehaviourを継承してもSOLID原則を適用できる?
A

もちろん適用できます! ただし、MonoBehaviourの継承は最小限にするのがポイントです。

Unityでは、MonoBehaviour を継承することで、スクリプトを GameObject にアタッチできます。ただし、何でも MonoBehaviour を継承すると、次のような問題が発生することがあります。

❌ 悪い例(不要にMonoBehaviourを継承)

public class PlayerCombat : MonoBehaviour
{
public void Attack() { /* 攻撃処理 */ }
}

この場合、Attack メソッドは特に MonoBehaviour の機能(Update()Start() など)を必要としていません。そのため、MonoBehaviourを継承する意味がありません

良い例(MonoBehaviourを継承せず、依存関係を減らす)

public class PlayerCombat
{
public void Attack() { /* 攻撃処理 */ }
}

public class Player : MonoBehaviour
{
private PlayerCombat combat = new PlayerCombat();

public void PerformAttack()
{
combat.Attack();
}
}

こうすることで、MonoBehaviour を必要なクラスだけに限定し、スクリプトの結びつきを減らせます

💡 MonoBehaviourを継承するかどうかの判断基準

  • スクリプトが Update()Start() を使うか?
  • GameObject にアタッチする必要があるか?
  • 他のスクリプトから独立して動作できるか?

これらを考慮しながら、MonoBehaviourの継承を最小限にし、通常のC#クラスを活用することで、SOLID原則を適用しやすくなります!

タイトルとURLをコピーしました