単純な状態遷移ならば有限状態機械で十分なのですが、
処理の割り込みや処理同士の依存関係などがある場合では有限状態機械では限界を感じます
そこで処理自体をオブジェクトとして扱ったほうが何かと便利なことが多いです
ここでは処理オブジェクトをタスクとして扱っています
つまり、タスクシステムとは、処理をオブジェクト単位(タスク)で扱い、
(タスク実行中でも)タスクの追加(+割り込み)・削除・取替に柔軟に対応できるシステムです
タスクシステム実装にあたり、メモリプールと呼ばれる連続メモリ領域をあらかじめ用意し、
そこから配置newでメモリ確保するようにしています
理由は通常のnewはメモリ領域の探索とメモリ確保が低速なため
頻繁に必要になる処理オブジェクトの生成処理でシステム全体が遅くならないようにするためです
また、処理に必要なメモリ量も自分で把握できるというメリットもあります
このように予め確保したメモリ領域を必要に応じて切り割りして割り当てる機能をアロケーターといいます
タスクには予め優先度の情報を持たせておき
タスクリストにタスクを優先度昇順になるように追加していき、優先度昇順に実行します
つまり、優先度の値が小さければ、先に処理されるため、
割り込みしたければ優先度の値を小さくしてタスクリストに追加します
さらにタスク生成のため、タスクのサイズ分確保できるメモリ領域の探索が必要になるわけですが
探索の高速化のためにタスクのメモリ上でのリストの実装もしてあります
これはカーソルが一周した場合や、割り込みなどによるタスクの処理順序変更で、
連続メモリが断片化していることがあるので
メモリ上順番の関係にあるタスク間の断片領域を計算し、
新たに追加するタスクを確保できる領域ならば追加、
そうでなければ、次の断片化領域に移るという処理を行います
この方法は1バイト単位で空き領域を探索するという手法よりはるかに高速です
&ref(): File not found: "list.jpg" at page "ゲームシステム講座3回";
連続メモリの容量は予め決められたサイズなので
大量のタスクが追加されてしまうと容量が足りなくなる場合が出てきます
(本来は、そうならないようなサイズを予め算出しておくのが好ましいが・・・)
その場合、メモリを拡張する必要があります
通常のポインタでタスク間の連結を実装してしまうと
メモリ拡張に伴い、そのまま全タスクをコピーしたとき
全タスクに対し、タスクリストのつなげ直しが必要になってしまいます
そのため、メモリプールの先頭アドレスからの相対位置をタスク同士の連結の情報として持っておきます
&ref(): File not found: "extend.jpg" at page "ゲームシステム講座3回";
今回の全ファイル
zip:tasksystem.zip
追記:今回ばかりは正直実装に疲れました
余力があれば
・デフラグ機能
・タスクリスト追加済みのタスクの優先度変更機能
・タスクリスト追加済みのタスクの差し替え機能
を追加したいと思います