TECHNOLOGIA


 まだ全部を移植したわけではないので、ホントにキモの部分しかありません(^^;
 また、動作試験用の変数なども入ったままです。
 さらに変数名が長いです(^^; 自分でわかりやすいように変えて使って下さい。

 私がこのシステムを知ったところでは、「アクトシステム」と呼ばれていました。
 そしてキャラ等の動きを記述したプログラム(要はタスク)を「アクト」と
 呼んでいました。たぶんActionから来たものだと思います。
 気に入らなければ適当に変えて下さい(笑)

 大元のソースは80x86アセンブラで書いてあります。(98専用(^^;)
 こちらの方は、スクロールマップ上を複数のキャラ(自キャラ含む)が動き
 簡単な会話や特殊動作まで入っています。ほしい方にはさしあげますが
 あまり期待しすぎないように。

 「ダブルバッファを使わずにV30マシンでもスクロールを実現するぜ計画」
 というアホな考えによって作ったものなので、表示系は参考にならないと思います。あとキー入力とかも適当です。どっちもバグ残ってるし。


action.hここから---------------------------------------------------------------

#define ACT_MAX 20 /*動作できるアクトの個数。とりあえず20個まで*/

/*----------------------------------------------------------------------
Action構造体(必要に応じて変更してください)
----------------------------------------------------------------------*/
typedef struct{
short ID; /* ID (0=空き) */
void (*fnAction)(); //Action処理ルーチン
short ctAction; //処理カウンタ
short ATTR; //状態
short posX; //ワールドX座標
short posY; //ワールドY座標
short vectX; //X方向移動ベクトル
short vectY; //Y方向移動ベクトル
short direction; //移動方向
short Speed; //移動速度
short SPNum; //表示開始スプライト番号
short sizSPX; //スプライトの横幅
short sizSPY; //スプライトの縦幅
short ctAnime; //アニメーションカウンタ
}Action;

static short activeActNum; /* 現在アクティブなAct */
static void (*ActFunc[ACT_MAX])(void); /*関数へのポインタの配列*/
static Action ActWork[ACT_MAX]; /* Action構造体の配列 */

/*----------------------------------------------------------------------
Action構造体初期化
----------------------------------------------------------------------*/
void InitAct( void ){

short i;

for (i=0;i<ACT_MAX;i++){
ActWork[i].ID = 0; /*すべてのActの動作停止*/

}
}
/*----------------------------------------------------------------------
Actionセット
ActWorkの空きを探し、あればセット。
なければ抜けてしまう。

とりあえずActのIDはすべて1ですが、引数で指定して
変えられるようにしたほうがいいでしょう。
----------------------------------------------------------------------*/
short SetAction( void *funcPtr ){

short i;

for (i=0;i<ACT_MAX;i++){
if (ActWork[i].ID==0){
ActWork[i].ID = 1;
ActWork[i].fnAction = funcPtr;
break;
}
}
}
/*----------------------------------------------------------------------
Actionリセット
----------------------------------------------------------------------*/
void ResetAction( short ActNum ){
ActWork[ActNum].ID = 0;
}
/*----------------------------------------------------------------------
Action処理
----------------------------------------------------------------------*/
void GoAction( void ){
short i;
for (i=0;i<ACT_MAX;i++){
activeActNum = i; /* 現在アクティブなAct番号 */
if (ActWork[i].ID!=0){
ActWork[i].fnAction();
}
}
}

action.hここまで---------------------------------------------------------------



【使い方】

・メインプログラム中に #include "action.h" を追加。

・初期化ルーチン中で、ActWork[]を初期化し、必要な個数のActをセット。

・メインループ中に GoAction(); を挿入。

 メインループは、
 ダブルバッファ切換え→OT初期化→GoAction();→ダブルバッファ切換え〜〜〜と
 なると思います。


【Actの組み方】

/*----------------------------------------------------------------------
 簡単なActの例
----------------------------------------------------------------------*/
void dummy(void){
FntPrint("Dummy¥n");
ActWork[activeActNum].counter = 2;
ActWork[activeActNumi].fnAction = dummy2;
}
void dummy2(void){
FntPrint("Dummy2¥n");
if (ActWork[activeActNum].counter-- == 0){
ActWork[activeActNum] = dummy;
}
}

 実はまだ、C言語上には完全に移植できてないのですが実用上は問題ないです。
 たとえば、上の例では最初のループ(処理落ちしない限り1/60)で Dummy と
 表示し、次の2回のループでは Dummy2 と表示します。そして最初の処理が
 呼ばれるよう元に戻しています。つまり、GoAction()を呼ぶだけで

 Dummy Dummy2 Dummy2 Dummy ・・・・・・・・・
 <---1/60---><---1/60---><---1/60---><---1/60--->・・・・・・・・・

 と実行されていく事になるわけです。

 当然、関数の内容によってはひとつの処理が1/60を越えてしまう事もありえます。

 要は何か条件が変わったら、呼ぶ関数を切換えているだけです。
 切換えなければ同じ関数が呼ばれ続けます。

 switch〜caseで処理を振り分けるよりは高速に処理でき(るはず)ます。
 ひとつの関数は、必要最小限の条件判断しかしないわけですから。

 ローカル変数を使ってないので、呼び出し時のオーバーヘッドが
 ほとんどないはずです(たぶん(^^;。80x86のCコンパイラなら確実)



 次はもう少し実用的?な例です。
 横スクロールシューティングでありがちな、ザコキャラみたいな感じですね。

/*----------------------------------------------------------------------
 実用的?な例
----------------------------------------------------------------------*/

※初期化の時にセットしておく

ActWork[このActの番号].vectX = -1;
ActWork[このActの番号].posX = 適当;
ActWork[このActの番号].posY = 適当;

そのほかのワークも適宜設定してください(^^;


void zako(void){ /* 16ドットX方向に移動 */

/* ATTRのビット0を当り判定用にしているとする */
if (ActWork[activeActNum].ATTR || 0x0001){
ActWork[activeActNum].counter = 16; /* 16/60[sec] */
ActWork[activeActNum].SPNum = 爆発スプライト
ActWork[activeActNum].fnAction = zako_death; /*爆発表示*/
}

/* 移動 */
ActWork[activeActNum].posX += ActWork[activeActNum].vectX;

/* 自キャラと当たったら、向こうのATTRに当たった事を知らせる */
/* (自キャラは専用のワークを持っておいた方がいいでしょう */
if (ActWork[activeActNum].posX == ActWork[MyShip].posX){
ActWork[MyShip].ATTR |= 0x0001;

/* そして自分も爆発する */

ActWork[activeActNum].counter = 16; /* 16/60[sec] */
ActWork[activeActNum].SPNum = 爆発スプライト
ActWork[activeActNum].fnAction = zako_death; /*爆発表示*/
}


if (ActWork[activeActNum].posX <= 0){ /* 画面右端なら反転させてしまう */
ActWork[activeActNum].vectX = 1;
}

if (ActWork[activeActNum].posX >= 320 && ActWork[activeActNum].vectX == 1){ /* 左に移動中で、画面左端なら消す */
ResetAction(activeActNum);
}

/* スプライトプリミティブに新しい座標値をセット */
/* いいかげんです(^^; */
sp->x = ActWork[activeActNum].posX;
sp->y = ActWork[activeActNum].posY;
/* スプライトプリミティブのオーダリングテーブルへの登録 */
GsSortFastSprite(sp, &WorldOT[activeBuff], 0);

}
void zako_death(void){ /* 爆発スプライトを16/60秒表示(一瞬(^^;) */
if (ActWork[activeActNum].counter-- == 0){
ResetAction(activeActNum); /* さよ〜なら〜 */
}

爆発アニメをさせるならここで(^^;

/* スプライトプリミティブのオーダリングテーブルへの登録 */
GsSortFastSprite(sp, &WorldOT[activeBuff], 0);
}



 たとえば違うY座標に4つ同じキャラを出したければ、4つワークを使用し、
 Y座標だけ変え、あとは同じデータ(関数へのポインタも含む)で初期化して
 使えばいいわけです。

 つまりActWorkは、同時に動作させたいキャラの「個数分」用意しなくては
 いけませんが、動作を記述したプログラム(アクト)はキャラクターの”種類分”
 あれば事足りる訳です。

 上のaction.hをそのまま使うと、20個しか動作させられません。(ACT_MAX)
 しかしプログラム(アクト)自体は40種類あってもかまいません。

 動かせるアクトの個数は、ActWorkをいくつ確保するかで決まります。
 アクトの種類は、いくつアクトプログラムを作るかで決まります。


 動きをすべてプログラムで記述してやると、バリバリ変形するボスキャラや、
 複雑な動きをするキャラ、条件が変化するたびに動きが変わるキャラなど容易に
 記述できます。たとえば条件によって会話データへのポインタを変えてやれば
 RPGやアドベンチャーにも使えますよね。世間一般に存在するゲームはほぼすべて
 記述できます。

 キャラでなくても「パレットを変更するアクト」とか「時間を表示するアクト」
 なんてのも使えます。(将棋とかの)思考ルーチンもアクトで記述すれば
 時計が止まったりしません。人間側のアクトは一時停止フラグとか用意して
 おけばいいわけで。




 この概念を利用して何か作品を作られたら

 「ネットやろうぜ内ではソースはすべて公開する。」
 (他で使った場合も可能な限りお願いします)
 
 「ネットやろうぜ内に限り改造・流用、そして公開自由!」

 ということにしてください。m(._.)m
 最後の「公開自由」については抵抗があるかもしれませんが(^^;

 結構、「ネットやろうぜ」ってプログラミング初心者が多いと思うんですよ。
 しかしノウハウはあまり公開されないし、誰も基本的な事は教えてくれない。
 それでも乗り越える人は乗り越えてくると思うんですが、最初はみな改造から入ると
 思うんです。
 でも昔と違って複雑になってしまいました。パソコンでもいきなりゲイツです。
 そしてソースは非公開があたりまえです。これじゃ新しい人が育ちにくい。


 アクトシステムを使えば、新しいアクトさえ作ってしまえばどんな事でもできます。(ほんまかいな)

 たとえば横シューを作ったとして、それが改造流用公開自由であれば、
 誰かが(ボス)キャラを作ってくれたり、
 別のステージを作ってくれたりする可能性があるわけです。
 もちろん、まったく別物のゲームを作ってしまう人が出てくるかもしれません。

 初心者は、可能な範囲で改造していくことで実際の現場でも使われている
 技法を修得する事ができます。いやマジで。



 私は、SFXVIみたいなシステムを作りたいナーと思っているわけです。
 (SFXVIの詳しい仕様は知りません)
 格闘ゲームのキャラを作る事からゲームプログラミングを覚えてもらえたら
 いいかなあ。絵だけの人もキャラデザインとかできますし、盛り上がるんじゃ
 ないかなあと思ってるんですが〜

 なんか疲れたっす。割り込みの件で。

 旅に出ようかと。


 「こんなのやりたいんだけどどうするんだ!」とかあったらメール下さい。
 可能な限り即日、このホームページ上でお答えします。

 なお、ネットやろうぜシステム自体の質問は受け付けかねます(笑)

 それではおやすみなさい。