タスクシステムについて
文章:.1064
日付:2004/10/09

目次
1.はじめに
2.タスクシステムとは
3.TCB






1.はじめに
今回はタスクシステムについて説明します。
タスクシステムは、ゲーム開発においては超基本的な技術です。
(とはいうものの、私は最近知ったのですが…(´▽`;)ヾ

今回の内容を作成するにあたり、以下のページを参考にさせていただきました。

「White Paper」
「Logician Lord」
「Atsushi's Homepage」
いずれのページも、現役のゲームプログラマーによる有益な情報があって、とても参考になります。

あと、参考書籍として、
「Windowsプロフェッショナルゲームプログラミング2」
を参考にさせていただきました。

タスクシステムについて、もっと詳しい知識を得たいのであれば、
これらの情報が役に立つと思います。




2.タスクシステム
タスクシステムとは、
複数のTCB(タスク・コントロール・ブロック)からなるシステムです。

タスクシステムの理解のために、ゲームプログラムのの基本的な流れを説明しておきます。

ゲームをC言語で実装すると、基本的には以下のようなると思います。
int main() {
    // 初期化処理
    while(1) {
        // ゲームメインループ
    }
    // 終了処理
}
例えば、自機と敵機がいるとすると、
int main() {
    // 初期化処理
    while(1) {
        // 自機の処理
        // 敵機の処理
    }
    // 終了処理
}
となります。

この場合にタスクシステムを利用すると、
int main() {
    // 初期化処理
    while(1) {
        // TCB1(自機)
        // TCB2(敵機)
    }
    // 終了処理
}
となります。

このように、自機キャラ/敵キャラなどをTCBというくくりで管理しようというのが、
タスクシステムです。

タスクシステムを採用すると、以下のメリットが得られます。
デメリットとしては、
現代的なプログラムに比べると、分かりにくい部分があります。

ジャンプ処理や、関数ポインタを使用するので、
泥臭いコードになりがちです。




3.TCB
 a.TCBとは何か
TCBとはC言語でいう「構造体」です。

TCBは、以下のメンバを持ちます。
@がメモリ節約のための仕掛けです。
Aがゲームらしいシステムを作るための仕掛けです。
BがCPUに負荷をかけないための仕掛けです。

では、なぜ、TCBを使うと、そういったメリットが得られるのかを解説します。

 b.なぜメモリを節約できるのか
TCBには、 という要素があるので、分かる人は、「これは連結リストだ!」と即答できると思います。
連結リストが分からない人は、「猫でも分かるプログラミング」などを
参考にしてください。

連結リストは、配列と比べて、 という特徴があります。

TCBでは、連結リストを拡張して、リングバッファというデータ構造になっています。
リングバッファとは、文字通り、連結リストが輪のようになっているデータ構造です。

具体的な例を見ていきます。
例えば、TCBが8つ確保してあり、自機と敵機1でTCB領域を使用しているとします。
リングバッファ1

そして、敵機2が現れると次のようになります。
リングバッファ2

ここで、敵機1を撃破すると次のようになります。
リングバッファ3

このように領域を使い回しすることにより、
メモリを最大限まで使用できるわけです。

 c.なぜゲームらしいシステムが作れるのか
タスクシステムがゲームらしいシステムを作るのに
有利な理由があります。

それは処理をフレーム単位で考えるからです。

フレームとは、
int main() {
    while(1) {
        // ゲームループ
    }
}
このゲームループの回数のことです。
例えば、1フレームといった場合、ループを一回することになります。

TCBのメンバである、 は1フレームの処理を想定しているので、
実装内容が分かりやすくなります。


もう一つ有利な理由があります。

それは、 です。

この優先順位というは、タスクの実行順位です。

ゲームは、キャラ同士が互いに影響を及ぼすことが多いです。
例えば、ボスキャラがやられたら、ザコ敵もいっしょに自爆するなど。
そういった処理を行う場合に、優先度が重要となってきます。
(ちなみにこの関係を親子タスクといいます。
 この例では、親がボスで、子がザコです)

具体的な例では、
名前優先順位
ボスTCB1
ザコ1TCB10
ザコ2TCB11
ザコ3TCB12
というTCBをセットしているとします。
この場合、タスクの実行順番は、
ボス→ザコ1→ザコ2→ザコ3
となります。

この状態で、ボスにタスクが回ってきた場合にボスがやられたとします。
その場合、ボスTCBは次フレームで消滅するように、消滅用の実行関数をセットします。
(これをタスクチェンジといいます)

ザコにタスクが回ってきた段階で、ザコTCBはボスの状態を見て、
「あ、ボスがやられたな」というのを確認するわけです。
そして、自身の消滅用の実行関数をセットします。

この仕組みを、親子タスクの仕組みを使わずに作ると、
子が親のヌルポインタを参照したり、
危険なことが起こりやすくなりわけです。


 d.なぜCPUの負荷がすくなるなるのか
CPUの負荷が少なくなる理由としては、
できるだけ分岐を減らして、ジャンプを使用するということにあります。

たとえば、オブジェクト指向でキャラクラスを定義します。

その場合に実行関数の中身は、以下のようになると思います。
switch (状態) {
case 止まる:
    // 動かない処理
case 歩く:
    // 歩く処理
case 走る:
    // 走る処理
・
・
・
・
}
というように状態に応じて、延々と分岐処理が必要となります。
そのため、無駄な判定処理をするため、処理が遅くなります。
(注:デザインパターンにStateパターン/Strategyパターンというものがあり、
これを使用すれば、分岐を少なくすることが可能です。)

こういった分岐を、TCBでは関数ポインタの差し替えによりなくしています。
(処理の差し替えをタスクチェンジといいます)

例えば、TCBで、ドラクエのようにマップでキャラが歩いているような
アニメーションを実装すると以下のようになります。
チェンジタスク
このように、関数をジャンプさせることで処理がを軽くなるわけです。



いかがでしたでしょうか。
タスクシステムは、ゲームシステムを構築するために、基本的な要素がかなり詰まっています。
これを知っていると、色々と応用が効くようになると思います。

ただ、ここでの内容は分かりやすく説明するため、省略している部分がかなりあります。

より詳しい情報は、参考ページで確認してくださいね。

以上、.1064でした。