タスクシステムについて
文章:.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というくくりで管理しようというのが、
タスクシステムです。
タスクシステムを採用すると、以下のメリットが得られます。
- CPUの負荷を減らすことができる
- メモリの使用量を節約することができる
- ゲームを開発するのに適したシステムで開発できる
デメリットとしては、
現代的なプログラムに比べると、分かりにくい部分があります。
ジャンプ処理や、関数ポインタを使用するので、
泥臭いコードになりがちです。
3.TCB
a.TCBとは何か
TCBとはC言語でいう「構造体」です。
TCBは、以下のメンバを持ちます。
- 前のTCBのポインタ…@
- 後のTCBのポインタ…@
- 優先順位…A
- 実行関数のポインタ…B
@がメモリ節約のための仕掛けです。
Aがゲームらしいシステムを作るための仕掛けです。
BがCPUに負荷をかけないための仕掛けです。
では、なぜ、TCBを使うと、そういったメリットが得られるのかを解説します。
b.なぜメモリを節約できるのか
TCBには、
- 前のTCBのポインタ…@
- 後のTCBのポインタ…@
という要素があるので、分かる人は、「これは連結リストだ!」と即答できると思います。
連結リストが分からない人は、「猫でも分かるプログラミング」などを
参考にしてください。
連結リストは、配列と比べて、
- 要素数の増減が可能
- データの挿入/削除が容易である
という特徴があります。
TCBでは、連結リストを拡張して、リングバッファというデータ構造になっています。
リングバッファとは、文字通り、連結リストが輪のようになっているデータ構造です。
具体的な例を見ていきます。
例えば、TCBが8つ確保してあり、自機と敵機1でTCB領域を使用しているとします。

そして、敵機2が現れると次のようになります。

ここで、敵機1を撃破すると次のようになります。

このように領域を使い回しすることにより、
メモリを最大限まで使用できるわけです。
c.なぜゲームらしいシステムが作れるのか
タスクシステムがゲームらしいシステムを作るのに
有利な理由があります。
それは処理をフレーム単位で考えるからです。
フレームとは、
int main() {
while(1) {
// ゲームループ
}
}
このゲームループの回数のことです。
例えば、1フレームといった場合、ループを一回することになります。
TCBのメンバである、
は1フレームの処理を想定しているので、
実装内容が分かりやすくなります。
もう一つ有利な理由があります。
それは、
です。
この優先順位というは、タスクの実行順位です。
ゲームは、キャラ同士が互いに影響を及ぼすことが多いです。
例えば、ボスキャラがやられたら、ザコ敵もいっしょに自爆するなど。
そういった処理を行う場合に、優先度が重要となってきます。
(ちなみにこの関係を親子タスクといいます。
この例では、親がボスで、子がザコです)
具体的な例では、
| 名前 | 優先順位 |
| ボスTCB | 1 |
| ザコ1TCB | 10 |
| ザコ2TCB | 11 |
| ザコ3TCB | 12 |
というTCBをセットしているとします。
この場合、タスクの実行順番は、
ボス→ザコ1→ザコ2→ザコ3
となります。
この状態で、ボスにタスクが回ってきた場合にボスがやられたとします。
その場合、ボスTCBは次フレームで消滅するように、消滅用の実行関数をセットします。
(これをタスクチェンジといいます)
ザコにタスクが回ってきた段階で、ザコTCBはボスの状態を見て、
「あ、ボスがやられたな」というのを確認するわけです。
そして、自身の消滅用の実行関数をセットします。
この仕組みを、親子タスクの仕組みを使わずに作ると、
子が親のヌルポインタを参照したり、
危険なことが起こりやすくなりわけです。
d.なぜCPUの負荷がすくなるなるのか
CPUの負荷が少なくなる理由としては、
できるだけ分岐を減らして、ジャンプを使用するということにあります。
たとえば、オブジェクト指向でキャラクラスを定義します。
その場合に実行関数の中身は、以下のようになると思います。
switch (状態) {
case 止まる:
// 動かない処理
case 歩く:
// 歩く処理
case 走る:
// 走る処理
・
・
・
・
}
というように状態に応じて、延々と分岐処理が必要となります。
そのため、無駄な判定処理をするため、処理が遅くなります。
(注:デザインパターンにStateパターン/Strategyパターンというものがあり、
これを使用すれば、分岐を少なくすることが可能です。)
こういった分岐を、TCBでは関数ポインタの差し替えによりなくしています。
(処理の差し替えをタスクチェンジといいます)
例えば、TCBで、ドラクエのようにマップでキャラが歩いているような
アニメーションを実装すると以下のようになります。

このように、関数をジャンプさせることで処理がを軽くなるわけです。
いかがでしたでしょうか。
タスクシステムは、ゲームシステムを構築するために、基本的な要素がかなり詰まっています。
これを知っていると、色々と応用が効くようになると思います。
ただ、ここでの内容は分かりやすく説明するため、省略している部分がかなりあります。
より詳しい情報は、参考ページで確認してくださいね。
以上、.1064でした。