Help us understand the problem. What is going on with this article?

オブジェクト指向はゲームづくりに向かない その1

「オブジェクト指向はゲームづくりに向かない」
という話を、何回かに分けて書きたいと思います。

多くのエンジニアは、オブジェクト指向プログラミングを学んで、現場で実践していると思います。
しかし、オブジェクト指向でゲームを設計していると、必ず突き当たる問題があります。

この連載では、その問題点を紹介し、解決策を提示したいと思います。

第一回は、「向かない点 その1 クラス継承には柔軟性がない」です。

向かない点 その1 クラス継承には柔軟性がない

オブジェクト指向言語には、継承という仕組みがあります。
コードを上手に再利用するために存在するこの機能。
これでゲームを設計すると、どんな感じになるでしょうか?

例として、見下ろし視点のアクションゲームを作っている、と想像してください。

ゲーム開発の場合、最初からすべての仕様が決まっていることはまずありません。
作っては試し、機能を追加して、また試す・・・を繰り返して、より面白いゲームとなる様に作っていくのが、通常です。

開発初期

まず開発の初期段階では、プレイヤーと敵がいて、動かして攻撃ができる、程度を作ることになるでしょう。
この時点でのクラス図は、こんな感じでしょうか。

inheritance1.png

ベースクラスとして GameObject を作り、位置(とそれに関わるロジック)を持たせる様にします。
GameObjectの子クラスとして、プレイヤーを示すPlayerと、敵を示すEnemyを作ります。
それぞれ、入力制御と、AIを持ちます。

これで、単純な敵とプレイヤーの動きが実装でき、戦えるようになりました。

1ヶ月後

さて、開発が始まって1ヶ月が経ちました。ここで企画から「空を飛ぶ敵を作りたい」という要望が来たとします。
空を飛ぶ敵の要件は

  • 障害物の上を通れる
  • プレイヤーの物理攻撃は当たらない
  • アイテムをドロップする際は、周囲の通れる場所にドロップする。

だったとします。
ここでプログラマーは考えます。ほぼEnemyと同じ動きだけど、一部特殊な動きになる。Enemyを継承したFlyingEnemyクラスを作ろう、と。
そしてクラス図はこうなります。

inheritance2.png

(この時点で、ある程度熟練したプログラマなら、いやいやいや・・これはないでしょって思うかも知れませんが、わかりやすい例として提示してますので!)

FlyingEnemyに、空を飛ぶ敵特有の機能を実装しました。
これで、空を飛ぶ敵の完成です。めでたしめでたし。

2ヶ月後

さらに1ヶ月が経過しました。
するとまた、企画かこんな要望が来ました。
「羽のアイテムを使うとプレイヤーは空を飛べるようにしたい」
要件としては、

  • プレイヤーは羽のアイテムを使うと空を飛ぶことができる
  • 空を飛んでいる間、障害物の上を通れる
  • 敵の物理攻撃は当たらない

さて、どうしましょう。
(アイテムを使う、という機能はもう実装してある事とします)

素直に実装すると、

inheritance3.png

こうでしょうか。
Playerクラスに、「飛んでいる時は物理攻撃が当たらない」という機能を実装しました。
同じ機能がFlyingEnemyPlayer2つに存在することになってしまいました。
この機能に修正が入った場合、2つの箇所に同じ修正を入れなければなりません。
新しく入ったプログラマーが、このことを知らずに、FlyingEnemyだけを修正してしまう、なんてことが起こってしまう恐れがあります。
あまり良くないですね。

では、共通部分はベースクラスに持っていくのが良いでしょうか?

inheritance4.png

一応、クラスの継承の機能をうまく使っている様にはなっています。
しかし、既にFlyingEnemyで動いていたコードを、ベースクラスに持っていくことで、エンバグのリスクが大きくなります。
(今回の例は単純化しているので、簡単そうに見えますが、実際のソースはもっと複雑に入り組んでいるものです)

そして、「飛ぶ」ということに関連する機能が、GameObjectFlyingEnemyに分散してしまっていることも、わかりにくいですね。

どっちのやり方も微妙です。スッキリしませんね。
とはいえ、今からクラス設計を1から見直すというのは、デバッグも1からやり直すことになるので、不可能です。

クラス継承の問題点

このように、クラス継承による設計には「途中から継承構造を変えるのが難しい」という問題点があります。
もし、最初からすべての仕様が決まっているのなら、そのとおりの素直な継承構造を作れば、大丈夫でしょう。
しかし、そんなことはゲーム開発ではあり得ません。

この問題を解決するには、どうしたら良いのでしょうか?
解決策は少し後に取っておいて、次回は、「向かない点その2 データの依存関係が見えない」を書いてみたいと思います。

参考記事:
http://whats-in-a-game.com/implementation-inheritance-is-evil/
https://codeburst.io/inheritance-is-evil-stop-using-it-6c4f1caf5117

mas-yo
オンラインゲームのサーバーをC++で作っています。 Rust勉強中。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした