緊急企画DirectXを使おうの巻・その3
DirectX拡張プラグインの使い方の説明も第3回となりました。今回は「当たり判定」とその他もろもろです。
●当たり判定もプラグインにおまかせ
今までは当たり判定を検出するのに多少面倒なことを行っていましたが、DirectXプラグインをつかえばスプライト同士の当たり判定を調べる機能というものがあるので、これを利用すれば比較的楽に処理を作ることができます。
HSPでの当たり判定のアルゴリズムを簡単に書いておきますと、任意の大きさの四角形を仮想的に生成し(下図左)、その四角形がお互いに重なったときに衝突と見なす(下図右)、というものです。ただしこれはいろいろ試した上での推測なので実際にこのようなアルゴリズムが使われているのかどうかはわかりません。
当たり判定を検出させるためにはまず当たり判定がどれだけの大きさなのかということを教えてやる必要があるわけですが、その設定は至って簡単です。キャラクタサイズを設定(es_size)するときに、3つ目のパラメータが当たり判定の大きさとなっていますので、そこへキャラクタサイズの何%の大きさを当たり判定にするのか指定するだけです。
あとは、プログラム中で当たり判定を行うだけですが、このままではすべてのスプライトが判定の対象となるため非常に効率が悪いです。そのため、キャラクタを自機・敵・自機の弾・敵の弾・障害物などというように分類してやりましょう。それにはes_type
p1,p2を使います。p1がスプライト番号で、p2がタイプ値(識別番号)で、1〜32768(正確には65535)まで指定することができますが、実際には16種類までしか設定することはできません。
マニュアルをみればわかりますが、タイプ値は2進数で管理するため、「自機は1、敵は2、自機の弾は3・・・」という風に設定してはいけません。自機の弾は自機(1)と敵(2)の両方の属性を持つものとして扱われてしまうので、自機の弾は3ではなく4(2の2乗)を指定することになります。
その他 | 障害物 | 敵の弾 | 自機の弾 | 敵(地上) | 敵(空中) | 自機 |
このあたりがよくわからないうちは、このように16列の表を作成し、それぞれが何を表すかを書き込んでいきます。そして、スプライトに割り当てたい属性に1を当てはめ、es_type時に2進数や16進数でタイプを代入することで、何となくわかってくるでしょう。たとえば、15番のスプライトを地上の敵にしたいときは、左から3番目が1になる(つまり、ビット2が1になる)ので以下のようになります。
es_type 15,%0000000000000100 es_type 15,$0004 es_type 15,4 |
3つとも同じ動作をします。とりあえずこれくらいなら10進数で入れた方がいいでしょう。
これで区別ができたので、当たり判定を行います。「es_check p1,p2,p3」を使います。p1に結果を代入する変数、p2に当たり判定を調べるスプライト番号、p3に当たり判定を調べるタイプ値を入れると、p2のスプライトがp3のタイプ値のスプライトと衝突しているかを調べ、衝突していればp1で指定した変数に衝突しているスプライト番号が、衝突していなければ-1が戻ります。上の表で、障害物・敵の弾・空中の敵がスプライト0番に登録されている自機と衝突しているかを変数hitに検出するには、このようにします。
es_check
hit,0,%00000000000110010 es_check hit,0,$0032 es_check hit,0,50 |
このように複数のタイプに対して判定を行うときは、なれるまでは2進数や16進数を使った方がわかりやすいと思います。
●フラグをいじる
es_flag p1,p2という命令を使うことで、スプライトのフラグをいじることができます。フラグをいじることで、キャラクタを点滅させたり画面内をはね回ったりさせることができるようになります。p1にスプライト番号を入れ、p2にフラグ値を入れます。フラグ値は、以下のようになっていて、適用させる値の合計値を代入します。
1-127 | カウントダウンタイマー | 数値が設定されると、1フレームごとに値がマイナスされていきます。 |
128($0080) | カウントダウン消失 | 1のときは、カウントダウンタイマーが0になってもスプライトが消滅しなくなります。 |
256($0100) | スプライト表示 | スプライトが存在する時点で自動的に1になるようなので、無視してもかまいません。 |
512($0200) | スプライト移動 | 0だとスプライトはその場で固定されますが、es_adirやes_aimなどを実行すると自動的に解除されます。 |
1024($0400) | 自由落下移動 | 1の時には重力に従って画面下へと自由落下を行い、画面下でバウンドします。 |
2048($0800) | BGとリンクして移動 | BG機能がまだありませんので、影響は受けません。 |
4096($1000) | ボーダー消去無効 | 1になっていると、スプライトがes_areaのエリアより外へ移動してもスプライトが消えません。 |
8192($2000) | ボーダーXで反転 | X座標がes_areaの端まで到達すると、移動方向を反転させます。 |
16384($4000) | ボーダーYで反転 | Y座標がes_areaの端まで到達すると、移動方向を反転させます。 |
32768($8000) | カウントダウン時点滅 | 0のときは、カウントダウンタイマーが作動した時にスプライトが点滅します。 |
例:16番のスプライトを、自由落下をしながら点滅し、50フレーム後に消滅するようにする。 es_flag 16,1074 es_flag 16,$432 es_flag 16,%0000010000110010 |
これらを使えば、復活時に自機を点滅させる処理なんてのは簡単にできてしまいます。ただし、タイマーが127(秒間60フレーム処理落ちなしで約2秒)までしかないので、それ以上の長さを必要とする場合はアニメーションと変数を使います。
$0800の「BGとリンクして移動」というのはBG(背景グラフィック)が現在サポートされていませんので、将来のために予約してあるだけということになります。どのような機能かというと、シューティングゲームの地上物のように画面のスクロールにあわせて移動させたい時に使うものだと予想されます。
●使ってみよう
ではこれらをふまえて「弾が相手にヒットしたら爆発させ、ヒットした弾を消去する」という処理を作りましょう。
repeat 4 TAMA=cnt+10 es_check HIT,TAMA,4 if HIT=-1 : continue es_get BOMBX,HIT,3 es_get BOMBY,HIT,5 repeat 8 es_new BOMB,256 es_set BOMB,BOMBX,BOMBY,8 rnd A,64 es_adir BOMB,A,300 es_type BOMB,128 es_flag BOMB,$8200+20 loop es_kill HIT es_kill TAMA loop |
es_checkで当たり判定を検出し、当たっていれば爆発のセットと敵・弾の消去を行います。あえて説明するほどの内容でもないので、爆発の設定を少し凝ってみました。
まず爆発を複数表示するために敵の座標を取得しています。これにはes_getという命令を使っていて、この命令を使うことでスプライトの座標やタイプ、フラグなどの値を取得できます。続いて空のスプライトを256番から検索し、見つかった番号を爆発のスプライトにセットします。そしてタイプやフラグを設定し、少し派手さを出すためにランダムな方向へ移動させるようにします。これを8回繰り返した後にes_killで敵と弾を消去し、終わりです。
命令の説明を一通り行っているので、あまり説明することはありませんね。注意点としては、弾が発射されていないときにes_checkがどのような結果を返すかわからないのでもしも妙な動作をするようであればes_findでスプライトを検索して見つからなければループを抜け出すようにするとうまく動いてくれます。
●次回は?
結局今回もマニュアルの受け売りで終わってしまいましたが、次回があればゲームを一本作るくらいのつもりでプログラム例をたくさん示してみようかと思います。つっかもう緊急企画じゃなくなってるような気が。