前回、JavaScriptで『スペースウォー!』(Spacewar!)を完成させたので、次に対戦型の『スペースウォー!』を作ります。ただそのときにちょっとした問題に直面したので、今回はその部分を記事にします。

これまでに作ってきた多くのゲームの当たり判定は対象物を円、または球とみなし、その中心の距離と半径の合計を比較するという方法をとってきました。しかし今回はプレーヤーが縦に長いため、この方法はとれません。JavaScriptの場合はcontext.isPointInPath(x,y)関数がありましたが、サーバーサイド(言語はC#)で当たり判定をする場合はどうすればいいのでしょうか?

GraphicsPathクラスを使った図形との当たり判定

C#にもこのような当たり判定をする方法は存在します。GraphicsPath クラスを使う方法です。

やり方は簡単で図形の頂点の座標の配列をGraphicsPath.AddLinesメソッドに渡しておき、当たり判定をしたい座標をIsVisibleメソッドに渡します。trueが返されればその座標は図形内部にあります。

以下はクリックされた座標が_pointsで構成される図形の内部にある場合はタイトルバーに”OK”、そうでない場合は”NG”と表示するWindowsFormsアプリケーションのコードです。

なんだ簡単じゃないか・・・とはなりません。ASP.NET Core でこれをやろうとするとサーバー上で実行しようとしたときに例外が発生します。これはSystem.Drawing.Common が Windows でしかサポートされないからです。サーバーのOSがLinuxの場合はうまくいかないのです。

GraphicsPathクラスを使わない図形との当たり判定

ではどうするか? GraphicsPathクラスを使わずに図形との当たり判定をする方法を考えます。

図形との内外判定をする方法のひとつにCrossing Number Algorithmがあります。今回はこのアルゴリズムを使います。このアルゴリズムは以下のようなものです。

「多角形の各線分ごとに、指定した点を通るx軸に平行な線との交点があるかどうかを調べ、交点が指定した点より右にあればカウントする。カウントした点が奇数なら指定した点は多角形に含まれている」

赤い水平線には交点が3つ(奇数)あります。青い水平線には交点が2つ(偶数)あります。たしかに前者は図形の内部にあり、後者は図形の外部にあります。

ただし、この方法には注意点があります。それは水平線が頂点を通る場合です。緑の水平線の交点は2つ(偶数)ですが、内部にあります。水平線が頂点を通る場合や辺と完全に重なってしまう場合は工夫しなければなりません。

そこで以下のようなルールを設けます。

上向きの辺は、開始点を含み終点を含まない。
下向きの辺は、開始点を含まず終点を含む。
水平な辺はカウントしない。

このルールのもとで以下のようなメソッドを定義します。

JavaScript編 図形との当たり判定

次にJavaScriptでもやってみます。

冒頭の動画のコード

冒頭の動画のコードを示します(解説は長くなるので省略)。

index.js