初版 2026/02/11
改訂


概要

ここでは、シューティングゲームなどで良くある「敵がプレイヤーを目掛けて弾を撃つ」処理を実現する。 弾の移動方向はは16方向とし、各方向の移動量の定義と、双方のキャラクターの位置関係から移動方向を求める処理の実装例を記載する。


方向の定義

上から右回りに1~16とする。
移動しない場合は、方向はゼロとする。

2026-02-11-msx-16direction_01.svg


移動量テーブル

上記の定義を元に、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;
}