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の詳しい仕様は知りません)
格闘ゲームのキャラを作る事からゲームプログラミングを覚えてもらえたら
いいかなあ。絵だけの人もキャラデザインとかできますし、盛り上がるんじゃ
ないかなあと思ってるんですが〜
なんか疲れたっす。割り込みの件で。
旅に出ようかと。
「こんなのやりたいんだけどどうするんだ!」とかあったらメール下さい。
可能な限り即日、このホームページ上でお答えします。
なお、ネットやろうぜシステム自体の質問は受け付けかねます(笑)
それではおやすみなさい。