【MSX】敵がプレイヤーを狙って16方向で弾を撃つ処理
初版 2026/02/11
改訂
概要
ここでは、シューティングゲームなどで良くある「敵がプレイヤーを目掛けて弾を撃つ」処理を実現する。 弾の移動方向はは16方向とし、各方向の移動量の定義と、双方のキャラクターの位置関係から移動方向を求める処理の実装例を記載する。
方向の定義
上から右回りに1~16とする。
移動しない場合は、方向はゼロとする。
移動量テーブル
上記の定義を元に、x、y座標の移動量を定義する。
最後に方向1と同じデータを定義することにより、後の方向算出処理で一周した時の判定を省略可能としている。
0.00, 0.00
0.00, -1.00
0.38, -0.92
0.75, -0.75
0.92, -0.38
1.00, 0.00
0.92, 0.38
0.75, 0.75
0.38, 0.92
0.00, 1.00
-0.38, 0.92
-0.75, 0.75
-0.92, 0.38
-1.00, 0.00
-0.92, -0.38
-0.75, -0.75
-0.38, -0.92
0.00, -1.00
方向テーブル(16 x 16 = 256bytes)
テーブルの(0,0)を起点とし、左から右に向かって画面の上方向とした、方向の値を定義したテーブル。
なお、16方向のため精度はあまり高くない(当たらない位置がある)。精度を高くする場合は、方向テーブルのデータ量を増やすよりは、方向を32方向とした方が良い。
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
5,3,2,1,1,1,1,1,1,1,1,1,1,1,1,1
5,3,3,2,2,1,1,1,1,1,1,1,1,1,1,1
5,4,3,3,2,2,2,2,1,1,1,1,1,1,1,1
5,4,3,3,3,2,2,2,2,2,1,1,1,1,1,1
5,4,4,3,3,3,2,2,2,2,2,2,2,1,1,1
5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,1
5,4,4,3,3,3,3,3,2,2,2,2,2,2,2,2
5,4,4,4,3,3,3,3,3,2,2,2,2,2,2,2
5,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2
5,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2
5,4,4,4,4,3,3,3,3,3,3,3,2,2,2,2
5,4,4,4,4,3,3,3,3,3,3,3,3,2,2,2
5,4,4,4,4,4,3,3,3,3,3,3,3,3,2,2
5,4,4,4,4,4,3,3,3,3,3,3,3,3,3,2
5,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3
座標の相関から方向を求めるロジック
BASICでは以下のようになる。
(X1,Y1)を弾を撃つキャラクター、(X2,Y2)を狙うキャラクターの座標としている。
1000 ' ===== SEARCH DIRECTION TABLE=====
1010 X1=(EX*8)\16:Y1=(EY*8)\16:X2=(PX*8)\16:Y2=(PY*8)\16
1020 IF X1<=X2 AND Y1>=Y2 THEN XX=(Y1-Y2):YY=(X2-X1):DP=1:GOTO 1060
1030 IF X1<=X2 AND Y1<=Y2 THEN XX=(X2-X1):YY=(Y2-Y1):DP=5:GOTO 1060
1040 IF X1>=X2 AND Y1<=Y2 THEN XX=(Y2-Y1):YY=(X1-X2):DP=9:GOTO 1060
1050 XX=(X1-X2):YY=(Y1-Y2):DP=13 'X1>X2 AND Y1>Y2
1060 BD=V((YY*16)+XX)+DP
1070 RETURN
以下で、方向テーブルに合わせて座標を16x16のブロックに分割。
1010 X1=(EX*8)\16:Y1=(EY*8)\16:X2=(PX*8)\16:Y2=(PY*8)\16
2つのオブジェクトの座標位置関係から、テーブルの参照先インデックス(XX、YY)と補正値(DP)を設定。
1020 IF X1<=X2 AND Y1>=Y2 THEN XX=(Y1-Y2):YY=(X2-X1):DP=0:GOTO 1060
1030 IF X1<=X2 AND Y1<=Y2 THEN XX=(X2-X1):YY=(Y2-Y1):DP=4:GOTO 1060
1040 IF X1>=X2 AND Y1<=Y2 THEN XX=(Y2-Y1):YY=(X1-X2):DP=8:GOTO 1060
1050 XX=(X1-X2):YY=(Y1-Y2):DP=12 'X1>X2 AND Y1>Y2
方向テーブルから取得した値に補正値を加えて、方向値を算出。
計算の結果、方向値が17となることがあるが、移動量テーブルの要素17に要素1と同じ値を設定することで、方向値を1に戻す判定をしないようにしている。
1060 BD=V((YY*16)+XX)+DP
以下サンプルプログラムで動作を確認できる。
https://msxpen.com/codes/-Ol9mvQydRpDFTKuB_Ia
参考:Cの実装
以下、z88dkでコンパイルを確認。
なお、この処理を行うにあたり、キャラクターの座標は16ビットで保持、上位8ビットの値を実際に表示する座標と扱う。
移動量テーブル
// 移動量テーブル
// 実数で扱うため、256倍しておく
int vx[] = { 0, 0, 97, 192, 235, 256, 235, 192, 97, 0, -97, -192, -235, -256, -235, -192, -97, 0 };
int vy[] = { 0, -256, -235, -192, -97, 0, 97, 192, 235, 256, 235, 192, 97, 0, -97, -192, -235, -256 };
方向テーブル
// 方向テーブル
uint8_t directionTbl[] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
5,3,2,1,1,1,1,1,1,1,1,1,1,1,1,1,
5,3,3,2,2,1,1,1,1,1,1,1,1,1,1,1,
5,4,3,3,2,2,2,2,1,1,1,1,1,1,1,1,
5,4,3,3,3,2,2,2,2,2,1,1,1,1,1,1,
5,4,4,3,3,3,2,2,2,2,2,2,2,1,1,1,
5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,1,
5,4,4,3,3,3,3,3,2,2,2,2,2,2,2,2,
5,4,4,4,3,3,3,3,3,2,2,2,2,2,2,2,
5,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2,
5,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2,
5,4,4,4,4,3,3,3,3,3,3,3,2,2,2,2,
5,4,4,4,4,3,3,3,3,3,3,3,3,2,2,2,
5,4,4,4,4,4,3,3,3,3,3,3,3,3,2,2,
5,4,4,4,4,4,3,3,3,3,3,3,3,3,3,2,
5,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3
};
方向算出処理
/*
* 方向取得処理
* 方向は上から右周りに1~17で返される
* 17は1と同等のため、移動量テーブルに方向1と同じデータを設定している。
*
* args:
* - x1 uint8_t 開始点のX座標
* - y1 uint8_t 開始点のY座標
* - x2 uint8_t 目標点のX座標
* - y2 uint8_t 目標点のY座標
*
* return:
* - uint8_t 方向値(1~17) ※17は1と同方向
*/
uint8_t get_direction(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
uint8_t XX, YY, DP;
uint8_t BD;
x1 = x1 >> 4;
y1 = y1 >> 4;
x2 = x2 >> 4;
y2 = y2 >> 4;
if (x1 <= x2 && y1 >= y2) {
XX = y1 - y2;
YY = x2 - x1;
DP = 0;
} else if (x1 <= x2 && y1 <= y2) {
XX = x2 - x1;
YY = y2 - y1;
DP = 4;
} else if (x1 >= x2 && y1 <= y2) {
XX = y2 - y1;
YY = x1 - x2;
DP = 8;
} else {
XX = x1 - x2;
YY = y1 - y2;
DP = 12;
}
BD = directionTbl[(YY << 4) + XX] + DP;
return BD;
}