クソゲーに魂を!プロジェクト(3)の続きです。今回はGameクラス(更新処理部分)を定義します。

インデントが深くなるので名前空間部分は省略して表記します。

残り時間の更新と時間切れ時の処理

時間の経過とともにバトルの残り時間を減らします。残り時間が 0 になったらバトル終了を意味するGameFinishedイベントを送信します。

移動処理

各キャラクタを移動させる処理を示します。

Playerの移動

Playerを移動させる処理を示します。PlayerクラスのUpdateメソッドを呼び出しているだけです。

弾丸の移動

弾丸を移動させる処理を示します。フィールドの縁にぶつかったら消滅させます。またこのときは死亡フラグがセットされたオブジェクトをリストから取り除く処理が必要になるので戻り値を返しています。

餌の移動

餌を移動させる処理を示します。フィールドの縁にぶつかる直前に跳ね返りの処理をおこないます。ところがフィールドの縁自身が移動しているため、跳ね返りの処理がうまくいかない場合があります。この場合は消滅させます。このときも死亡フラグがセットされたオブジェクトをリストから取り除く処理が必要になるので戻り値を返しています。

あたり判定

各キャラクタのあたり判定と衝突時におこなう処理を示します。

Player同士のあたり判定

Player同士のあたり判定の処理を示します。Playerの頭が他のPlayerの身体に衝突したときは体長が短いほうが死にます。そこでPlayerの頭と他のPlayerの身体が衝突しているかどうかを高速で判定する必要があるのですが、ここではイベントソートのアルゴリズムを採用しています。

頭が他のプレイヤーの身体に衝突するための必要条件はX座標を比較したときにその差の絶対値が(頭の半径+身体の半径)より小さいことです。また頭や身体の半径は体長によって変化しますが、上限値を設定しているので、(頭の半径+身体の半径)は36を超えることはありません。

そこで頭と身体の中心のX座標、頭の中心のX座標に36を足したもの、頭の中心のX座標から36を引いたものをソートします。そして座標が小さいものから処理をしていきます。ループのなかで取り出されたものが頭の中心のX座標から36を引いたものであればHashSetに追加し、頭の中心のX座標に36を加えたものであればHashSetから削除します。

ループのなかで取り出されたものが身体であれば、HashSetのなかに格納されているものがあたり判定となるものの候補となります。これだとすべてを総当りで調べる必要はないので高速で当たり判定をすることができます。

死亡したPlayerオブジェクトはAllPlayersリストから取り除かないといけないのでHitCheckPlayerToPlayerメソッドは戻り値を返しています。

Playerの死亡処理

死亡処理を示します。死亡したPlayerは餌になって他のPlayerの養分になってもらいます。そのあと死亡フラグをセットして身体を消滅させます。また死亡カウントをインクリメントします。これは復活時に新規参加時よりも短くする(ミスに対するペナリティ)ためのものです。

また死亡判定によって生き残っているPlayerが1つのみになったときはそのPlayerが勝者です。このときは勝者に与えるボーナスポイントを計算したあとGameFinishedイベントを送信します。

BodyToFoodメソッドは引数で渡されたCircleの近くに餌を出現させます。

Playerと弾丸のあたり判定

Playerと弾丸のあたり判定の処理を示します。Playerの身体に弾丸が命中したら体長が短くなり、餌が放出されます。ここでも当たり判定にはイベントソートのアルゴリズムを採用しています。

餌とPlayerの頭の当たり判定

餌とPlayerの頭の当たり判定の処理を示します。餌と衝突した場合はスコア加算の処理をおこないます。

Playerはフィールド外に出ていないか?

以下はPlayerがフィールド外に出ていないかを判定する処理です。判定の処理は頭の位置とフィールドの半径を比較しているだけです。場外判定された場合、NPCは中央に跳ね返り運動をさせます(それまでに回避行動をするのでこの処理はおこなわれないはず)。ユーザーの場合は死亡処理をおこないます。

NPCの行動決定

NPCの行動を決定する処理を示します。

まず壁にぶつかりそうになったら回避行動を優先します。壁にぶつかりそうになっているかどうかはフィールドの半径と自身の中心からの距離を比較すればわかります(IsOutOfFieldメソッドで判定している)。壁への衝突回避は進行方向を中心方向に変更することでおこないます。

それ以外のときは自分よりも短いユーザーがいたらその方向に回頭させます(NPC同士で撃ち合いはしないようにする)。ただしすべてのNPCがひとりのユーザーを集中攻撃してしまってはよくないので、これをやるのは一体だけです。また自分よりも長いユーザーがいたら逃げるようにします。これらの処理は常におこなうのではなく、NPCのIntelligenceの値によっておこなったりおこなわなかったりと幅を持たせます。

更新処理の全体

更新処理の全体を示します。更新処理8回に1回の割合でフィールドを狭くしていきます。そのあとNPCの行動決定の処理をいれたあとPlayer、弾丸、餌の更新処理をして当たり判定の処理をおこないます。

当たり判定があった場合は死亡フラグが立っているオブジェクトをリストから取り除く処理をおこないます。ただしユーザーのPlayerが死亡してもPlayersリストからは取り除きません。これはゲームオーバー後も描画処理を継続したいからです(このとき死亡位置が必要となる)。

スコアランキング

スコアランキングはひとつのプレイ(スタートボタンを押下してゲームオーバーになるまで)と同じユーザーが繰り返し参加したときの累計ランキングの2つをつくります。

スコアランキングを実装するために以下のクラスを定義します。

次にScoreRankingクラスですが、インデントが深くなるので名前空間部分は省略して表記します。

まずフィールド変数を示します。

データを保存する処理を示します。普通のランキング用と累計ランキング用のふたつのJsonファイルに書き込みをしています。

保存されたデータを取得する処理を示します。