前回 Z80 で左右判定を行いました。Z80 ではアナログの扱いではなく 8方向で表現してた事もあり、各方向毎に専用判定を記述することで対応しました。C# ではベクトルで座標を、向きはベクトルや角度で扱っているため、角度毎の専用処理では判定できません。そこで数式を用いて判定することになります。それが外積や内積です。今回は数学的な説明としてではなく、道具としての外積と内積を解説していきます。


  • 判定前の準備
外積や内積は、3D では法線の算出やポリゴンの裏表の判定などに使われています。これを 2D 空間に当てはめると、進行方向に対して、相手がどちらにいるかを判定できたりします。計算はベクトル同士で行われるので、相手の位置と自分の向き、どちらもベクトルである必要があります。

自分の向きが角度で表現されていたら、それは単位ベクトルに変換します。単位ベクトルは最大長さが 1 となる正規化されたベクトルです。角度からそのまま三角関数を呼び出すだけで単位ベクトルを取得できます。X成分は cos、Y成分は sin を使います。角度の単位はラジアンなので、必要に応じて変換します。

float ag = Angle * Deg2Rad; Vector2 dir = new(MathF.Cos(ag), MathF.Sin(ag));

Angle は自分の向きです。Deg2Rad はデグリーからラジアンに変換する定数で、中身は MathF.PI / 180f となっています。デグリー 360度 = ラジアン 2π なので、デグリーをラジアンに変換するには 2π を掛けて 360 で割ります。分母分子ともに 2 で約分して、π÷180 が変換定数となります。

相手の位置は、最初から XY座標ではありますが、その座標はワールド座標で表されていますので、それを自分を基準位置としたローカル座標に変換します。

Vector2 other = new(target.X - mypos.X, target.Y - mypos.Y);

この計算式では、mypos が自分自身、target が相手の位置を示しています。自分の座標値を減算することで、自分を中心としたローカル座標に変換しています。

これで、2つのベクトル dir と other を使って判定できるようになりました。


  • 左右判定
左右判定は外積を使います。2D 空間の外積は、上記の変数を使った場合は下記のように計算して求めます。

var cross = (int)((dir.X * other.Y) - (dir.Y * other.X)) / 4;

これで cross に計算結果が入ります。細かく判定する必要はなかったので、計算結果を 1/4 の整数にしています。細かく左右判定をしたければ、計算結果の小数まで見るのも良いかと思いますが、あまり細かく見すぎると、キャラの動きが揺れてしまう事があるので注意が必要です。1/4 しているのは動作結果からの調整値です。
※ 1/4 は調整値なので本来は定数定義しておくのが良いです。

この得られた cross の値が正であれば、相手は向かって右側にいます。負の値であれば左側にいます。そして、ゼロであれば直線上のどこかに相手がいることになります。


  • 前後判定
直線上にいると言っても、それは自分の向かって正面にいるのか、背後にいるのか分からない状態です。そこで、左右判定の結果がゼロであれば、改めて今度は前後関係について計算をすることにします。ここで使うのが内積です。自分の向き dir と、相手の位置 other を用いた内積計算は以下のようになります。

float dot = (dir.X * other.X) + (dir.Y * other.Y);

この dot の値が正であれば、相手は自分の前方にいることになります。負の値なら後方です。参考までにゼロなら真横になります。先に左右判定を行ってから前後判定をしている場合は、結果がゼロはあり得ないので、ここは無視して問題ありません。

具体的な実装例は、下記のサンプルをダウンロードして見ていただくのが一番早いかと思います。皆様のご参考になれば幸いです。

20231212-2000
※ LANケーブルをPC直差しではなくこのアダプターを介するとネットの速度が上がるかもしれません。まあ、LANや光回線の速度にも影響は受けると思いますが、この値段、かつ国内メーカーという事でオススメします。