Unity制作でオブジェクト指向を使わない場合

Unity経験者にとってオブジェクト指向に苦手意識を持っている場合、オブジェクト指向を使わない場合の大変さを知るためには、オブジェクト指向の利点と、それを使わない場合に直面する具体的な問題を対比させることが効果的です。以下のポイントを参考にするといいでしょう

1. コードの再利用性とメンテナンスの問題

オブジェクト指向の利点:

  • クラスやオブジェクトを使って、再利用可能なコードを作成できます。
  • 共通の機能を持つ複数のオブジェクトを簡単に作成し、メンテナンスがしやすくなります。

言い換えると・・・

  • 似たような機能を持つコードを何度も書かずに済むようになります。
  • コードを修正するのが簡単になります。

オブジェクト指向を使わない場合:

  • 同じ機能を持つコードを何度も書くことになります(コードの重複)。
  • 変更が必要な場合、すべての箇所を手動で修正する必要があります。

// オブジェクト指向を使わない場合
public class GameManager : MonoBehaviour
{
    public GameObject enemyPrefab;

    void Start()
    {
        CreateEnemy1();
        CreateEnemy2();
    }

    void CreateEnemy1()
    {
        GameObject enemy1 = Instantiate(enemyPrefab);
        enemy1.name = "Enemy1";
        enemy1.GetComponent<Enemy>().health = 100;
        enemy1.GetComponent<Enemy>().attack = 10;
    }

    void CreateEnemy2()
    {
        GameObject enemy2 = Instantiate(enemyPrefab);
        enemy2.name = "Enemy2";
        enemy2.GetComponent<Enemy>().health = 150;
        enemy2.GetComponent<Enemy>().attack = 15;
    }
}
// オブジェクト指向を使う場合
public class GameManager : MonoBehaviour
{
    public GameObject enemyPrefab;

    void Start()
    {
        CreateEnemy("Enemy1", 100, 10);
        CreateEnemy("Enemy2", 150, 15);
    }

    void CreateEnemy(string name, int health, int attack)
    {
        GameObject enemy = Instantiate(enemyPrefab);
        enemy.name = name;
        enemy.GetComponent<Enemy>().Initialize(health, attack);
    }
}
public class Enemy : MonoBehaviour
{
    public int Health { get; private set; }
    public int Attack { get; private set; }

    public void Initialize(int health, int attack)
    {
        Health = health;
        Attack = attack;
    }
}

2. 複雑なシステムの管理

オブジェクト指向の利点:

  • 継承やポリモーフィズムを利用して、複雑なシステムをシンプルに管理できます。
  • クラス間の関係を明確に定義することで、システム全体の理解が容易になります。

言い換えると・・・

  • 複雑なシステムをシンプルに管理できます。
  • コードの関係がわかりやすくなります。

オブジェクト指向を使わない場合:

  • 大規模なプロジェクトでは、コードの依存関係が複雑になり、バグが発生しやすくなります。
  • 各機能の修正や追加が困難になります。

言い換えると・・・

  • コードの依存関係が複雑になり、バグが発生しやすくなります。
  • 新しい機能を追加するのが大変になります。

// オブジェクト指向を使わない場合
public class GameManager : MonoBehaviour
{
    public GameObject zombiePrefab;
    public GameObject vampirePrefab;

    void Start()
    {
        CreateZombie("Zombie1", 100, 10);
        CreateVampire("Vampire1", 150, 20);
    }

    void CreateZombie(string name, int health, int attack)
    {
        GameObject zombie = Instantiate(zombiePrefab);
        zombie.name = name;
        // ゾンビ固有の初期化処理
        zombie.GetComponent<Enemy>().health = health;
        zombie.GetComponent<Enemy>().attack = attack;
    }

    void CreateVampire(string name, int health, int attack)
    {
        GameObject vampire = Instantiate(vampirePrefab);
        vampire.name = name;
        // ヴァンパイア固有の初期化処理
        vampire.GetComponent<Enemy>().health = health;
        vampire.GetComponent<Enemy>().attack = attack;
    }
}
// オブジェクト指向を使う場合
public class GameManager : MonoBehaviour
{
    public GameObject zombiePrefab;
    public GameObject vampirePrefab;

    void Start()
    {
        CreateEnemy<Zombie>("Zombie1", 100, 10);
        CreateEnemy<Vampire>("Vampire1", 150, 20);
    }

    void CreateEnemy<T>(string name, int health, int attack) where T : Enemy
    {
        GameObject enemyPrefab = typeof(T) == typeof(Zombie) ? zombiePrefab : vampirePrefab;
        GameObject enemy = Instantiate(enemyPrefab);
        enemy.name = name;
        enemy.GetComponent<T>().Initialize(health, attack);
    }
}
public abstract class Enemy : MonoBehaviour
{
    public int Health { get; protected set; }
    public int Attack { get; protected set; }

    public abstract void Initialize(int health, int attack);
}

public class Zombie : Enemy
{
    public override void Initialize(int health, int attack)
    {
        Health = health;
        Attack = attack;
        // ゾンビ固有の初期化処理
    }
}

public class Vampire : Enemy
{
    public override void Initialize(int health, int attack)
    {
        Health = health;
        Attack = attack;
        // ヴァンパイア固有の初期化処理
    }
}

3. 拡張性の欠如

オブジェクト指向の利点:

  • 新しい機能や要素を追加する際に、既存のコードに最小限の変更で済むように設計できます。
  • 継承やインターフェースを使って、拡張性の高いコードを書くことができます。

言い換えると・・・

  • 新しい機能や要素を簡単に追加できます。
  • 既存のコードをあまり変更せずに新しい要素を追加できます。

オブジェクト指向を使わない場合:

  • 新しい要素を追加する際に、大量のコード修正が必要となり、エラーが発生しやすくなります。
  • 拡張性が低く、新しい要求に対応するのが困難です。

言い換えると・・・

  • 新しい要素を追加する際に、大量のコード修正が必要になります。
  • 新しい要求に対応するのが困難です。

// オブジェクト指向を使わない場合
public class GameManager : MonoBehaviour
{
    public GameObject swordPrefab;
    public GameObject bowPrefab;

    void Start()
    {
        CreateSword("Sword", 10);
        CreateBow("Bow", 15);
    }

    void CreateSword(string name, int damage)
    {
        GameObject sword = Instantiate(swordPrefab);
        sword.name = name;
        sword.GetComponent<Weapon>().damage = damage;
        // 剣固有の初期化処理
    }

    void CreateBow(string name, int damage)
    {
        GameObject bow = Instantiate(bowPrefab);
        bow.name = name;
        bow.GetComponent<Weapon>().damage = damage;
        // 弓固有の初期化処理
    }
}
// オブジェクト指向を使う場合
public class GameManager : MonoBehaviour
{
    public GameObject swordPrefab;
    public GameObject bowPrefab;

    void Start()
    {
        CreateWeapon<Sword>("Sword", 10);
        CreateWeapon<Bow>("Bow", 15);
    }

    void CreateWeapon<T>(string name, int damage) where T : Weapon
    {
        GameObject weaponPrefab = typeof(T) == typeof(Sword) ? swordPrefab : bowPrefab;
        GameObject weapon = Instantiate(weaponPrefab);
        weapon.name = name;
        weapon.GetComponent<T>().Initialize(damage);
    }
}
public abstract class Weapon : MonoBehaviour
{
    public int Damage { get; protected set; }

    public abstract void Initialize(int damage);
}

public class Sword : Weapon
{
    public override void Initialize(int damage)
    {
        Damage = damage;
        // 剣固有の初期化処理
    }
}

public class Bow : Weapon
{
    public override void Initialize(int damage)
    {
        Damage = damage;
        // 弓固有の初期化処理
    }
}

4. 保守性の低下

オブジェクト指向の利点:

  • コードのモジュール化が進み、保守が容易になります。
  • バグ修正や機能追加が一部のクラスやメソッドに限定され、他の部分に影響を与えにくくなります。

言い換えると・・・

  • コードがモジュール化され、保守が容易になります。
  • バグ修正や機能追加が簡単になります。

オブジェクト指向を使わない場合:

  • コードの修正が全体に波及しやすく、バグの原因となることが多いです。
  • どの部分を修正すべきかが不明確になり、デバッグが困難になります。

// オブジェクト指向を使わない場合
public class GameManager : MonoBehaviour
{
    public GameObject enemyPrefab;
    public GameObject player;

    void Start()
    {
        // ゲーム開始時の初期化処理
        SpawnEnemies();
        InitializePlayer();
    }

    void Update()
    {
        HandlePlayerInput();
        UpdateEnemies();
    }

    void SpawnEnemies()
    {
        for (int i = 0; i < 10; i++)
        {
            GameObject enemy = Instantiate(enemyPrefab);
            enemy.name = "Enemy" + i;
            enemy.GetComponent<Enemy>().health = 100;
            enemy.GetComponent<Enemy>().attack = 10;
        }
    }

    void InitializePlayer()
    {
        player.GetComponent<Player>().health = 200;
        player.GetComponent<Player>().attack = 20;
    }

    void HandlePlayerInput()
    {
        // プレイヤーの入力処理
    }

    void UpdateEnemies()
    {
        // 敵の更新処理
    }
}
// オブジェクト指向を使う場合
public class GameManager : MonoBehaviour
{
    private EnemySpawner enemySpawner;
    private PlayerInputHandler playerInputHandler;

    void Start()
    {
        enemySpawner = new EnemySpawner();
        playerInputHandler = new PlayerInputHandler();
        enemySpawner.SpawnEnemies();
    }

    void Update()
    {
        playerInputHandler.HandleInput();
    }
}
public class EnemySpawner
{
    public GameObject enemyPrefab;

    public void SpawnEnemies()
    {
        for (int i = 0; i < 10; i++)
        {
            GameObject enemy = Instantiate(enemyPrefab);
            enemy.name = "Enemy" + i;
            enemy.GetComponent<Enemy>().Initialize(100, 10);
        }
    }
}
public class PlayerInputHandler
{
    public GameObject player;

    public void HandleInput()
    {
        // プレイヤーの入力処理
        player.GetComponent<Player>().HandleInput();
    }
}
public abstract class Character : MonoBehaviour
{
    public int Health { get; protected set; }
    public int Attack { get; protected set; }

    public abstract void Initialize(int health, int attack);
}
public class Enemy : Character
{
    public override void Initialize(int health, int attack)
    {
        Health = health;
        Attack = attack;
        // 敵固有の初期化処理
    }

    void Update()
    {
        // 敵の更新処理
    }
}
public class Player : Character
{
    public override void Initialize(int health, int attack)
    {
        Health = health;
        Attack = attack;
        // プレイヤー固有の初期化処理
    }

    void HandleInput()
    {
        // プレイヤーの入力処理
    }
}

これらのポイントを具体的な例とともに、オブジェクト指向を使わない場合の大変さがより明確に伝わるでしょう。また、実際にOOPを使ったコードを書いてみて、その利点を実感することで理解が深まるはずです。

Unity

Posted by hidepon