ドラゴンクエスト風RPGの作り方



DxLibFanに戻る

タスク処理を用いてドラクエ風RPGを作る。

タスク処理とは?



タスク処理とは?
実は、私も本当に最近知ったことなんです。
私も、実は完全な定義を知らないんですが説明します^^;大丈夫かよ!?
タスク処理とは仕事単位に関数を分けて、その関数達を関数のポインタの配列に入れて ループさせてひとつひとつ実行する処理のことらしいです。(なんか解りにくいですね^^;)
こんな感じです。 なんか、ステップ処理に慣れていると見にくいですよね。このようなものは。

さて、これは本当に原始的なものですので、 実際は、構造体に関数のアドレスとデータを入れて、 時が来たら構造体を通して構造体のデータを関数のアドレスを通して渡して実行させると言うものです。
なんか凄い分かり難い日本語ですね^^;どうしよう。 よって、私が参考になったサイトを紹介します。
DirectX Programers Page ない場合は・・・
コンピューターゲームのからくり googleのキャッシュに頼って!

まぁ、こんな感じなんです。
ちなみに書いているとおり作成した構造体は双方向リンクリストで繋ぎます。
こうすることでリンクを辿って行き、関数を実行して、次のリンクに行き、とそのように処理していきます。
(一応断っておきますが、構造体の配列でも出来ます。しかし実行優先順序がないのでこれではなにかと不便です。 すべて同じ様な処理だったら一部に構造体の配列を使うことをお勧めしますが^^;順序なしリスト構造ってやつです。)
では、実装はいったいどうなっているのかを見てみましょう。
なんと!DirectX Programmers Pageに「基本的に全てのサンプルはフリーなので自由に使用してやってください。 」 と書かれています!DirectX Programmers Pageの管理人さんに感謝感謝。謝謝(シェシェ(中国語!?))
このHPにはC++版とC言語版がありますが、まぁC++だと分かりにくそうなのでC言語版の解説で行きます。

では早速、C言語版タスク処理のエンジンを見てみましょう。
(余談:このソースは擬似タスク処理らしいが、まぁ私はタスク処理って事にしちゃってますけどいいのかなぁ?)

task.h
task.cpp


どうですか?なんとなく掴めましたか?
では、ソースの詳しい解説をします。(私の解説でわかる人は天才だと思う。)

task.hより

1.一回だけコンパイラに読み取ってもらう
0006: #ifndef __TASK_H_
0007: #define __TASK_H_

とありますが、これはなんのためなんでしょう?
分かる人は説明するな!と叫びたくなるくらい分かるんですが、一応説明。
これはヘッダファイルに定義されている構造体やプロトタイプ関数を二重インクルードしないために つけられているのです。
0073: #endif
73行目に#endifって書かれていますでしょ^^
つまり #ifndef __TASK_H_ は「もし、__TASK_H_が定義されていなかったらコンパイルする」って意味で、
#define __TASK_H_ は「__TASK_H_というものを定義する」という意味で、

#endif は「ここでifndefの範囲は終わりですよ」って意味です。
つまり、C言語のif文風で書くと、

void test(){
	static int flag=0;
	if(flag==0){//ここは#ifndef __TASK_H_ と思えばよい
	  flag=1;//ここは#define __TASK_H_ と思えばよい
	  /なにかを実行。(つまり#ifndefで言うと、構造体の定義とか)
	
	}//ここは#endifと思えばよい
}

以上のような感じです。
C言語風だとこれで一回しか実行しない部分が出来ましたね^^
それと同じ様にコンパイラに指示を出しているのがあの#ifndefなんです。
ちなみに#ifndefは「〜〜が定義されていなかったら実行する」ですが、 #ifdefは「〜〜が定義されていたら実行する」です。



2.タスク実行プライオリティって何さ?
0013: // タスク実行プライオリティ
0014: enum {
0015:     PRIO_TOP,// トップタスク
0016:     PRIO_00,
0017:     PRIO_01,
0018:     PRIO_02,
0019:     PRIO_03,
0020:     PRIO_04,
0021:     PRIO_05,
0022:     PRIO_06,
0023:     PRIO_07,
0024:     PRIO_08,
0025:     PRIO_09,       
0026:     SYSTEM_PRIO,// システムプライオリティ(基本的に削除しない)
0027:     PRIO_BOTTOM // テイルタスク
0028: };
これは何?プライオリティって何?
えーっと 私が調べたところによると・・・[優先順位。優先権。]だそうです。
なるほど。
これはタスクの実行順序のための定義だな!そう見るしかありませんね。
では、タスク処理の優先順位って何?って思っている方!?いますか!?
コンピューターゲームのからくりをよみましたか?
まぁ、構造だけでも^^以下のような感じです。

  • 構造体TOPとTAILがある。TOPは双方向リストの一番最初でTAILが双方向リストの一番最後である。
  • タスク処理は前にも書いたように構造体にデータと関数のポインタを持たせて実行するとき構造体のデータを 関数のポインタを通して関数に渡してその関数を実行するのである。
  • データと関数のアドレスが入った構造体(タスク)は必要になったら双方向リストに追加。
  • 双方向リストのつなぐ順序は TOP <-> このサンプルでは数字が低い順にTOPに繋がれる <-> TAIL  また、構造体の中に入った関数のアドレスの実行はTOPから始まってTAILで終わる。
  • メモリ資源は有限(mallocしないで配列から確保したほうが処理が高速だから, また、malloc等でメモリが足りなくなってきた時、ハードディスクが回って処理が遅くなったりしないようにするため) なので、タスクを沢山追加していくとかならずメモリが足りなくなる。よっていらないタスクを明示的に削除する関数も必要。

なんか、もっと分からなくなったような(TT)
ごめんなさい。
えーっと・・・・・・・・・ つまり!このプライオリティを定義しているenumは 双方向リスト構造の順序に使う識別子を定義しているのだ!
以上!



3.タスクコンディション?分かるか!ヴォケナス
0030: // タスクコンディションデファイン
0031: enum {
0032:     TASK_SYS,// システムタスク(トップ、テイルタスク)
0033:     TASK_ON,// タスク実行中
0034:     TASK_OFF,// タスク登録待ち
0035:     TASK_KILL,// タスク削除待ち
0036:     TASK_SLEEP// スリープ中 ZZZ...
0037: };
さて?「なにこれ?今度もなんか独自の定義が出てきたなぁ、覚えるのめんどくさ〜」
 とか思っているといつまでたっても分からないまんまです。この定義がある理由を理解しましょう^^

さて、タスクは双方向リンクリストで繋がった構造体でその中のコアは関数関数に渡すデータと前、説明しましたよね^^
かなり回りくどくて分かりにくい説明でしたが^^;^^;^^;アセアセ
なので、今は使わない関数を一時停止とかしたい訳ですよ。そういうのに使われますね。タスクコンディションは。
よって、TASK_SLEEPってのが定義されてますよね^^; で、タスクが普通に実行中のときはTASK_ON;タスク登録待ちの時はTASK_OFFって書いていますよね^^; そのとおりです。
これらをタスク構造体のタスクコンディションを保存する変数に入れてif文でそれらのコンディションに応じて分岐してタスクに登録されている関数を実行するわけです。
はい、もう言う事ありません。それでは。次へ!



4.本命の双方向リスト構造型構造体。タスクワーク?え?charで何が出来るのよ!?
0009: #define u_char      unsigned char
0010: #define TASK_MAX    256             // タスクの登録総数
0011: #define TASK_WSIZE  256             // ワークサイズ

0040: // タスクワーク定義
0041: typedef struct task_tag {
0042:     int         stat;            // タスク状態
0043:     int         type;            // タスクタイプ
0044:     int         prio;            // 実行プライオリティ
0045:    
0046:     task_tag    *pre;            // 前部リンク
0047:     task_tag    *next;          // 後部リンク
0048:     u_char      work[TASK_WSIZE];// ユーザー用(256byte)
0049:     void        *proc;         // 実行関数へのポインタ
0050:     void        *end;          // デストラクタ
0051: } TASK;
さて、やっと本命の双方向リスト構造(双方向リンクドリスト(Linked List)とも言う)(以下「タスク」と記する)の説明にきました。
これが肝です。本当に^^
このタスクワークって書いてますが、タスクワークはu_char work[TASK_WSIZE(256)]の事だと私は解釈しています。(以下、タスクワークはu_char work[TASK_WSIZE]の事を指す)
まずは、メンバの説明から^^

0042:int stat; // タスク状態
これは、タスクコンディションを入れるところ。

0043:int type; // タスクタイプ
これは、タスクワークに入れる構造体の識別子を入れる。(まだここは説明していない)

0044:int prio;// 実行プライオリティ
これは、enumで定義したプライオリティを入れる

0046:task_tag *pre;// 前部リンク
これは、次に優先順位が低いタスクへのポインタ。

0047:task_tag *next;// 後部リンク
これは、次に優先順位が高いタスクへのポインタ。

0048:u_char work[TASK_WSIZE];// ユーザー用(256byte)
これは、後で定義した独自構造体を無理矢理キャストしてデータを保持する空間。

0049:void *proc;// 実行関数へのポインタ
これは、毎回実行する関数へのポインタ

0050:void *end;// デストラクタ
これは、タスクが無効になったとき(要らなくなった時)に削除する関数内で呼ばれる関数へのポインタ
いわいるC++で言うデストラクタ。

書くのが疲れたので一時休戦

DxLibFanに戻る DxLibのダウンロードは以下から
Dxライブラリ置き場
DxLib Copyright(C) 山田 巧

The program using DxLib.
The text of this page.
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送