「オブジェクト指向はゲームづくりに向かない」
という話を、何回かに分けて書きたいと思います。
多くのエンジニアは、オブジェクト指向プログラミングを学んで、現場で実践していると思います。
しかし、オブジェクト指向でゲームを設計していると、必ず突き当たる問題があります。
この連載では、その問題点を紹介し、解決策を提示したいと思います。
第一回は、「向かない点 その1 クラス継承には柔軟性がない」です。
向かない点 その1 クラス継承には柔軟性がない
オブジェクト指向言語には、継承という仕組みがあります。
コードを上手に再利用するために存在するこの機能。
これでゲームを設計すると、どんな感じになるでしょうか?
例として、見下ろし視点のアクションゲームを作っている、と想像してください。
ゲーム開発の場合、最初からすべての仕様が決まっていることはまずありません。
作っては試し、機能を追加して、また試す・・・を繰り返して、より面白いゲームとなる様に作っていくのが、通常です。
開発初期
まず開発の初期段階では、プレイヤーと敵がいて、動かして攻撃ができる、程度を作ることになるでしょう。
この時点でのクラス図は、こんな感じでしょうか。
ベースクラスとして GameObject
を作り、位置(とそれに関わるロジック)を持たせる様にします。
GameObject
の子クラスとして、プレイヤーを示すPlayer
と、敵を示すEnemy
を作ります。
それぞれ、入力制御と、AIを持ちます。
これで、単純な敵とプレイヤーの動きが実装でき、戦えるようになりました。
1ヶ月後
さて、開発が始まって1ヶ月が経ちました。ここで企画から「空を飛ぶ敵を作りたい」という要望が来たとします。
空を飛ぶ敵の要件は
- 障害物の上を通れる
- プレイヤーの物理攻撃は当たらない
- アイテムをドロップする際は、周囲の通れる場所にドロップする。
だったとします。
ここでプログラマーは考えます。ほぼEnemy
と同じ動きだけど、一部特殊な動きになる。Enemy
を継承したFlyingEnemy
クラスを作ろう、と。
そしてクラス図はこうなります。
(この時点で、ある程度熟練したプログラマなら、いやいやいや・・これはないでしょって思うかも知れませんが、わかりやすい例として提示してますので!)
FlyingEnemy
に、空を飛ぶ敵特有の機能を実装しました。
これで、空を飛ぶ敵の完成です。めでたしめでたし。
2ヶ月後
さらに1ヶ月が経過しました。
するとまた、企画かこんな要望が来ました。
「羽のアイテムを使うとプレイヤーは空を飛べるようにしたい」
要件としては、
- プレイヤーは羽のアイテムを使うと空を飛ぶことができる
- 空を飛んでいる間、障害物の上を通れる
- 敵の物理攻撃は当たらない
さて、どうしましょう。
(アイテムを使う、という機能はもう実装してある事とします)
素直に実装すると、
こうでしょうか。
Player
クラスに、「飛んでいる時は物理攻撃が当たらない」という機能を実装しました。
同じ機能がFlyingEnemy
とPlayer
2つに存在することになってしまいました。
この機能に修正が入った場合、2つの箇所に同じ修正を入れなければなりません。
新しく入ったプログラマーが、このことを知らずに、FlyingEnemy
だけを修正してしまう、なんてことが起こってしまう恐れがあります。
あまり良くないですね。
では、共通部分はベースクラスに持っていくのが良いでしょうか?
一応、クラスの継承の機能をうまく使っている様にはなっています。
しかし、既にFlyingEnemy
で動いていたコードを、ベースクラスに持っていくことで、エンバグのリスクが大きくなります。
(今回の例は単純化しているので、簡単そうに見えますが、実際のソースはもっと複雑に入り組んでいるものです)
そして、「飛ぶ」ということに関連する機能が、GameObject
とFlyingEnemy
に分散してしまっていることも、わかりにくいですね。
どっちのやり方も微妙です。スッキリしませんね。
とはいえ、今からクラス設計を1から見直すというのは、デバッグも1からやり直すことになるので、不可能です。
クラス継承の問題点
このように、クラス継承による設計には「途中から継承構造を変えるのが難しい」という問題点があります。
もし、最初からすべての仕様が決まっているのなら、そのとおりの素直な継承構造を作れば、大丈夫でしょう。
しかし、そんなことはゲーム開発ではあり得ません。
この問題を解決するには、どうしたら良いのでしょうか?
解決策は少し後に取っておいて、次回は、「向かない点その2 データの依存関係が見えない」を書いてみたいと思います。
参考記事:
http://whats-in-a-game.com/implementation-inheritance-is-evil/
https://codeburst.io/inheritance-is-evil-stop-using-it-6c4f1caf5117