したかみ ぶろぐ

Unity成分多め

デザインパターンを勉強して今までを振り返る

デザインパターンを勉強した

オブジェクト指向の基礎は情報処理試験、授業、本等で学んできましたが、正直どう使用すればいいのかどのように設計すればよいかわかりませんでした。そんな中、友人が「デザインパターンを勉強しよう!」と提案して頂いたので実際に本を買って読んでみました。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

その結果、今まで自分が考えてきたコードが様々なデザインパターンに当てはまることが分かったのでいくつかまとめていこうと思います。

オブジェクト指向を実践すること

オブジェクト指向は昔から知っており、一度C++でカードゲームを実装した際にも使用しながら勉強しました。しかし、Unity・C#で使ってみたのは去年ぐらいからでした。

実際に使用して感じたことは

  • トップダウンな開発
  • コードの量が減る
  • コードが読みやすくなる
  • 仕様変更、追加が楽に出来る
  • etc

とかなりいいこと尽くめなのでやったこと無いって方は簡単にでもいいのでやってみてはいかがでしょうか

オブジェクト指向の例

継承

敵の動きを以下のように作成したとします。

public abstract class Enemy : MonoBehaviour {
   protected abstract void Move();

   void Update() {
       ......  // 何かしらの処理
       ......

       Move();
       ......
   }
}


Move関数は抽象メソッドで、Update関数はそのMove関数を使用しています。抽象メソッドがあるクラスはインスタンス化(GameObjectに追加等)出来ません。なので、このクラスを継承したクラスを作成します。

public class TakkleEnemy : Enemy {

   protected override void Move() {
       // プレイヤーに向かっていく処理
       ......
   }
}

これで、EnemyクラスのMove関数をオーバーライドして定義することが出来ました。このクラスは抽象クラスではないのでインスタンス化出来ます。また、EnemyクラスでUpdate関数を定義しているのでTakkleEnemyクラスではUpdate関数を定義する必要はありません。
この他にも敵の動きを作りたい場合はEnemyクラスを継承したクラスを作成して、Move関数を定義すれば様々な動きをする敵を作成することが出来ます。

このようなデザインパターン「Template Methodパターン」と言うそうです。

(このサイトが分かり易いです : デザインパターン「Template Method」 - Qiita)


正直、これだけだと有難みはあまり感じません。しかし、もしEnemyクラスに体力を制御する機能があったり、攻撃する関数が実装されている場合はMove関数だけを書くだけになるので楽になります。

多態性

他のゲームオブジェクトからEnemyクラスをGetComponentする際、継承をしようしていなかった場合、次のようになったりします。ここではtagから判断してクラスを取得しています。

      ......
      Enemy enemy;
      TakkleEnemy takkleEnemy;
      ......   // 他のEnemyクラス

      if(tag.Enemy)
         enemy = GetComponent<Enemy>();
      else if(tag.TakkleEnemy)
         takkleEnemy = GetComponent<TakkleEnemy>();
      .......    // 他のGetComponent

場合分けによってどのクラスが追加されているか判断しています。後々tagがどんどん増えますし、設定忘れなどで思うような動作をしないことがあります。と言うより、昔一度このようなスクリプトを書いてとても苦い思いをしました。


次に、Enemyクラスを継承している場合のコードが以下のようになります。

      .....
      Enemy enemy;
      if(tag.Enemy)
         enemy = GetComponent<Enemy>();
      .....

これだけで動作するようになります。これはオブジェクト指向の要素の一つ、多態性(多様性、ポリモーフィズム等)というもので継承された子クラスは親クラスとして扱うことが出来ます。

この機能によって新しくEnemyクラスを継承したクラスを作成しても他のコードの修正をする必要がありません。


使用したことがあるデザインパターン

ここで今まで使用したデザインパターンを軽く紹介しようと思います。

Singletonパターン

「Unity Singleton」と調べても簡単に出てきます。シーン上に1つしかないという条件がありますが、その代わりどこからでも簡単にアクセスできます。よく使用するオブジェクトや情報をまとめる際に使用しました。

Strategyパターン

先程のTemplate Methodパターンに似ているデザインパターンです。Template Methodパターンは実行する手順の中に抽象メソッド等があるものですが、Strategyは手順、アルゴリズムをごっそり変えるパターンです。このサイト(【デザインパターン】Template MethodとStrategyの違い - 緑茶思考ブログ)で分かり易く解説しています。

Mementoパターン・Commandパターン

最近開発したコンテンツでこの2つを使用してUndo機能を実装しました。Mementoパターンでは状態を保存する機能を作り、その中でCommandパターンを使用してUndo処理を作成しました。最後にこの機能をSingletonクラスのスタックに保存してUndoを行えるようにしました。


オブジェクト指向を使って楽をする

先程のようにオブジェクト指向を使用することによって開発を楽に行えるようになります。ゲーム開発においてオブジェクト指向を使用することによって次のような効果があると感じました。

  • コードの量が減る
    • デバッグする範囲が小さくなる
    • コードを考える範囲が減る
  • コードが読みやすくなる
    • デバッグが楽になる
    • 時間を置いても理解しやすい
  • 仕様変更、追加が楽に出来る
    • 時間を掛けずに編集が可能
    • 多くの機能を追加できる

ゲームに直結しないように感じるかもしれませんが、簡単にいうと「様々な敵、UIがすぐ作れる」「デバッグに苦労しない」「時間が掛からない分ゲームデザインに集中できる」などかなりの恩恵を受けます。


私自身まだまだオブジェクト指向デザインパターンを使いこなせてる自信がないのでこれからも開発、学習を通して良いプログラムが書けるよう精進します!