読者です 読者をやめる 読者になる 読者になる

Flat Leon Works

アプリやゲームを作ってます。

【C++ ゲームプログラミング】タスクシステムからの脱却を考える

C++ 技術

タスクシステムのメリット

タスクシステムのメリットは、ゲームの構成要素が作りやすくなりそして扱いやすくなる点です。

  • 作りやすくなる : 更新処理と描画処理のみ実装すればよい
  • 登場/退場させやすくなる : タスクを追加、削除するだけ
  • 勝手に動いてくれる : 勝手にタスクの更新/描画処理を呼んでくれる

ゲームの画面内にはたくさんのオブジェクトがあります。それらは各々で動いたり動かなかったりそれぞれ別の動作を行います。そしてよく現れよく消えます。このゲーム内のオブジェクトを実装するのにタスクシステムはとても合致しています。

タスクシステムの問題点

タスクシステムは便利なものですが問題点もあります。

  • 更新/描画の順番を制御できない
  • 特定のタスクを探すのに向いていない(全部のタスクを個別にチェックする必要がある)
  • プログラムの設計がタスクシステムに囚われがちになる
    • 別のシステムを作成すべきなのにタスクシステム内に実装してしまう( シーン管理タスク、衝突管理タスクなど )
  • これらに起因する設計とパファーマンスの悪さ

タスクシステムのタスクはシンプルな考え方なので使いやすいのですが、シンプルゆえに細かな制御が難しいという点があります。汎用的すぎるとも言えます。 また、タスクが便利なゆえに、すべてをタスクで済ませようとしていまいがちです。

タスクシステム不要論

そもそもタスクシステムって必要でしょうか。確かにタスクシステムがあれば簡単にゲーム内の表示物を実装することができます。ですがこの仕組は、C++ならstd::listと仮想関数で簡単に実現できます[参考]。ならば、もっと具体的なシステムを用意したほうが良いのではないでしょうか。

例えばタスクシステムではなく、ゲーム空間を表すゲームワールドというクラスを作ります。ゲームワールドクラスはキャラクターリスト(プレイヤー、敵)、カメラ、地形コリジョン、背景データ(マップチップなど)を持ちます。ゲームワールドの更新処理では、キャラクター、カメラの更新処理、キャラクターへの重力の加算処理、地形との衝突処理、キャラクター同士の衝突処理などを順番に行います。ゲームワールドの描画処理では、カメラ位置を設定し、背景データから背景の描画、キャラクターの描画を行います。デバッグ機能として地形コリジョンを表示する機能を持たせてもいいかもしれません。

タスクシステムという汎用的なシステムをやめゲームワールドという具体的なシステムを作ったことで、処理の順番が明確になり、具体的なデータへのアクセスも簡単になっています。プログラムの見通しがよくなったと思いませんか?

タスクシステムが生まれた背景

タスクシステムは昔から伝わる手法です。昔というのがポイントです。昔はC言語でプログラミングが行われていたので、std::listも仮想関数も無かった上にメモリ資源(とCPU資源)が厳しかったというのがタスクシステムが利用された理由だと思います。メモリ資源が厳しいので、各タスクは固定長のメモリが用意されそれを使い回すことで、高速なメモリ割り当てを実現していました(らしい)。そしてこれらを実装したタスクシステムは、std::listと仮想関数の組み合わせによる実装よりずっと手間がかかるものなので、必要に応じて具体的なシステム(ゲームワールドのような)を用意するのが大変だったんだと思います。

逆に言うと、現代ではstd::listも仮想関数もある上にメモリも潤沢にあるのでタスクシステムにこだわる必要はないということです。

タスクシステムがまだ有効な場面

タスクシステム不要論を説明しましたが、それでもタスクシステムが有効な場面というのはあります。

  • 小規模なゲーム
  • 気合と根性がある場合
  • タスクシステムを大局的に使う場合

小規模なゲーム

小規模なゲームではタスクシステムで十分戦えるかもしれません。

気合と根性がある場合

気合と根性があればどんなに大規模なゲームでもタスクシステムでやっていけると思います。意外と、なんとかなるものです。

タスクシステムを大局的に使う場合

今まで話してきたタスクシステムは描画物の管理としてのタスクシステムでした。実はタスクシステムには全体的なプログラムの管理としての使い方もあります。ゲームプログラムが毎フレーム行うべきことはキャラクターの更新だけではありません。例えば、ゲームパッドからの入力を集計したり、UIを更新したり、シーン管理を行うなどです。これらをタスクにしておけば、ゲームプログラム全体の毎フレームの流れを制御することができます。基本的にこれらの処理はどのゲームでも決まった順番で行われるのでタスクシステムにせずソースコードに直接書いてもいいのですが、タスクシステムにしておくと新しい機能を追加しやすくなります。例えば、処理時間を計測するプロファイルタスクを作り任意の場所に追加することで任意の場所に処理時間計測を追加することができます。

ただし、このような用途の場合はタスクシステムではなくモジュールシステムとでも呼んだほうがいいような気がします。

まとめ

  • 現代ではタスクシステム的なものはstd::listと仮想関数で簡単に実現でき、メモリ資源にも余裕があるのでタスクシステムにこだわる必要はない
  • 具体的なシステムを作ることでプログラムの見通しがよくなる
  • ただし小規模なゲームはタスクシステムで十分かもしれない
  • タスクシステムには大局的な使い方もある

【次回予告】タスクシステムの派生系