ASP.NET Coreで3DのSpaceWar!のような対戦型ゲームをつくります。こんな感じのゲームができます。

以前、WindowsFormsでSpaceWar!を真似たゲームをつくりました。それが下の動画です。

Spacewar!をC#で作る

スペースウォー!(Spacewar!)は1962年、当時マサチューセッツ工科大学(MIT)の学生であったスティーブ・ラッセルを中心に開発された、宇宙戦争をモチーフとした対戦型コンピューターゲームです。世界初のシューティングゲームとされています。今回はオンラインで対戦できるような3Dゲームとしてつくってみることにしました。

ゲームの仕様

自機はこれを真後ろから見た状態で中央に表示されます。Spacewar!の大きな特徴として、宇宙船に慣性が働くこと、画面中心の太陽に重力が存在し、宇宙船の動作に影響を与えることが挙げられます。方向転換しただけでは進行方向を変更することはできず、停止するためには移動方向と逆方向を向いて加速しなければなりません。ところがこれをやろうとすると操作性が悪くなりすぎるので採用しないことにしました。

同時に対戦できる数は「8」です。それに満たない場合はNPCで埋め合わせます。

とりあえずの完成品ができたので、いつもお世話になっているT.Umezawaさんに見ていただいたのですが、ここでいくつかの改善案が示されました。

↑キーで下降、↓キーで上昇にできないか?
一発死亡ではなくシールド制にしてはどうか?
レーダーをつけてほしい。
プレイヤー名も表示させる。

そこでこれらにも対応したものを公開します。

SpaceWarGameクラスの定義

名前空間はSpaceWarとします。そしていつもどおりGameクラスを定義します。

以降は名前空間を省略して以下のように書きます。

定数とフィールド変数

SpaceWarGameで定義するのは各定数とプレイヤーのリストの管理です。定数を定義した部分を示します。

シールド制にすると立て続けにやられてすぐに死亡してしまわないようにダメージをうけたときは約1秒間無敵状態にします。自機死亡後、次の自機が登場したときの無敵時間は約3秒です。

Playerオブジェクトを格納するリストを示します。

_playersはサーバーサイドに接続したときに付与されるIDとオブジェクトの辞書です。_npcsはNPC、_playersWithoutNPCはNPCではない普通のプレイヤー、_allPlayersは両方です。_playersと_npcsがあればそこから_playersWithoutNPCと_allPlayersは取得できるのですが、処理に時間がかかると処理落ちしてしまうため、最初にフィールド変数に格納しておき、すぐに結果を返せるようにしています。

初期化の処理

初期化の処理を示します。

コンストラクタは空っぽです。初期化はユーザーが接続したとき接続ユーザー数が1の場合におこないます。_allPlayersと_npcsをクリアして8個のNPCをつくります。

新しいPlayerを追加と削除

新しいPlayerを追加する処理を示します。

引数はサーバーサイドに接続したときに付与されるIDです。すでに辞書_playersのなかに登録されている場合はなにもしません。NPCが存在しない場合もそのプレイヤーはゲームに参加できないのでなにもしません。

それ以外のときはNPCを1つ_npcsから取り除き、新しいPlayerオブジェクトを生成してから_playersに追加します。そして他のフィールド変数も変更しないといけないのでSetAllPlayersメソッドを呼び出しています。

SetAllPlayersメソッドを示します。新しいプレイヤーがゲームに参加するときと離脱するときに_playersと_npcsだけでなく_playersWithoutNPCと_allPlayersも変更しなければなりません。その処理をおこなっています。

プレイヤーがゲームオーバーになったときとゲームの途中で離脱したときにはRemovePlayerメソッドが実行されます。引数で渡されたキーに対応するPlayerオブジェクトを削除し、代わりにNPCを追加します。そのあとSetAllPlayersメソッドを呼び出します。

プレイヤーの取得

GetPlayerメソッドは引数で渡されたidに対応するPlayerオブジェクトを返します。

以下のメソッドはとくに説明は不要かと……。

爆発時の処理

爆発時に発生する火花を生成する処理を示します。

Sparkクラスは以下のように定義されています。

コンストラクタの最初の3つは発生場所の座標、後ろの3つはXYZ方向への移動速度です。