Slither.io (スリザリオ)のようなオンラインゲームを作りたい(1)の続きです。独自のクラスを定義することで当たり判定の高速化を実現します。

定数について

名前空間はSnakeGameとします。インデントが深くなるので以降は名前空間部分は省略して書きます。

定数の意味は以下のとおりです。

Circleクラスの定義

餌やスネークの身体を描画するためにCircleクラスを定義します。

Circleオブジェクトは更新処理のたびに大量に生成されるため、使い回すことにします。

Createメソッドが実行されるとすでにストックがある場合はそれを返し、ない場合は新たに生成します。Pushメソッドは不要になったCircleオブジェクトをストックに追加します。

CirclesMapクラスの定義

Circleオブジェクト同士の当たり判定を高速におこなうためにCirclesMapクラスを定義します。ここではフィールド全体を一辺の長さが100の正方形に分割します。

Cellクラスの定義

一辺の長さが100の正方形の領域(以下、セルと表記する)を操作するためにCellクラスを定義します。

描画用のCircleオブジェクトと異なり当たり判定で使うものは間隔がある程度空いていても機能するので描画用のものと当たり判定用のものにわけます。更新処理で次々にCircleオブジェクトが追加されたり削除されるのですが、当たり判定用のものは8回に1回だけ保存します。また当たり判定用のものは必要なオブジェクト群を高速で取得できるようにPlayerIDをキーとして保存します。

GetCirclesForDrawメソッドとGetCirclesForHitCheckメソッドは描画用、当たり判定用のCircleオブジェクトのリストを取得するためのものです。

以下のメソッドはセルのなかにどのようなCircleオブジェクトが存在するかを返します。

CirclesMapクラスの初期化

CirclesMapクラスを以下のように定義します。最初にフィールド変数とコンストラクタを示します。

フィールドは円形なので正方形に分割されたセルのなかには完全にフィールドの内部に含まれるもの、完全にフィールドの外部に存在するもの、一部のみがフィールドの内部に含まれるものにわかれます。餌やプレイヤーの初期位置を決めるときに完全にフィールドの内部に含まれるセルを知っておきたいので、コンストラクタのなかでこれを取得しています。

Circleオブジェクトの追加・削除・移動

CirclesMap内にCircleオブジェクトを追加する処理を示します。追加したいCircleオブジェクトの座標をCellSizeで割ればどのセルに追加すればよいかがわかります。追加したら_circleCountをインクリメントします。追加したCircleオブジェクトが餌の場合(PlayerIDが負数のとき)は_foodCountもインクリメントします。

CirclesMap内からCircleオブジェクトを削除する処理を示します。やっていることは追加とほとんど同じです。

格納されているCircleオブジェクトの位置を移動させる処理を示します。移動元と移動先の座標から所属するセルを変更する必要がある場合は変更します。

周辺にプレイヤーがいないセルを取得する

GetCellsNoPlayerメソッドはNPCではないプレイヤーが存在しないセルのリストを返します。またGetCellsNoPlayerNoNpcメソッドはNPC含めたプレイヤーが存在しないセルのリストを返します。

フィールドに餌を追加する

ゲームが進行にともなってフィールド上の餌がなくなっていきます。そこでフィールドに餌を追加する機能を追加します。これを実行するセルは餌がひとつもなく完全にフィールドの内部に存在するものだけです。

描画用のCircleオブジェクトを取得する

クライアントサイドに送信する描画用のCircleオブジェクトを取得する処理を示します。GetNearCellsメソッドは指定された座標を中心とする幅 width, 高さ height の領域のCellオブジェクトのリストを返します。

GetNearCirclesメソッドは指定された座標を中心とする幅 width, 高さ height の領域のCircleオブジェクトのリストを返します。

当たり判定

引数として渡されたCircleオブジェクトが餌やそれ以外のプレイヤーに接触していないかを調べる処理を示します。

引数として渡されたCircleオブジェクトがセルの端にある場合は隣接するセルも同時に確認しなければなりません。そこで最初に当たり判定の対象になりそうなCircleオブジェクトを取得してそのあとオブジェクトの半径の和と中心の距離を比較して当たり判定をおこないます。

NPCによる衝突回避

NPCの衝突回避行動に関する処理を示します。まず距離が100以内で自分に一番近い位置にある他のプレイヤーのCircleオブジェクトを取得します。該当するものがあればその方向とは反対側に回頭させます。回頭する場合は以降の24回更新は右または左にターンさせます。