これまでは対コンピュータ対戦型のぷよぷよもどきのゲームを作ってきましたが、対人対戦型のものをつくります。動画は対コンピュータ対戦のものですが、リンク先は対人対戦型のものです。

これまで対人対戦型のゲームはいくつか作成してきましたが、基本的に途中から参加でき、途中で抜けることができるタイプのものでした。最初に自分の対戦相手を決めるタイプのものは作ってこなかったので、これを機に作ってみることにします。

その方法ですが、ページにアクセスしてプレイヤーとして登録したときにすでに登録されている人がいればその人との対戦がはじまり、誰も登録されていなければ誰から登録するまで待ち続けることにします。ふたりを1セットにしてGameクラスのインスタンスを作成してそこでゲームに関する処理をおこないます。対コンピュータ戦のようなアルゴリズムを考える必要はないと安易に考えていたら、けっこうハマりどころがありました。

ではさっそく作成することにします。名前空間はPuyoMatchとします。C#で書かれたソースはPages\PuyoMatchに置きます。それからNET 6.0をエックスサーバーにインストールするで示している手順が完了していることを前提としています。

流用するクラス

PuyoType列挙体とPuyoクラス、FallingPuyoクラス、Fieldクラスは対コンピュータ対戦型でつかっていたものをそのまま使います。ASP.NET Core版 対コンピュータ対戦できるぷよぷよをつくる(1)を参照してください。

PuyoMatchGameクラスの定義

PuyoMatchGameクラスを定義してそこでFieldクラスのインスタンスを2つ生成します。キー操作によるぷよの移動処理や送り込まれるおじゃまぷよの数の計算はここでおこないます。落下させた場合の結果や実際におじゃまぷよを落下させる処理はPuyoクラスでおこなわれます。

プレイヤーがふたりいるので、それぞれの状態を管理するためのPlayerInfoクラスを定義します。ここに格納されるのはプレイヤーの名前、AspNetCore.SignalRで接続したときのIDです。またHTMLでプレイヤー名が表示されるので名前のなかに <や>があったときにこれをエスケープした文字列を取得するためのプロパティも定義しておきます。

次にPuyoMatchGameクラスの本体の定義ですが、名前空間は省略します。

と書くべきところを以下のように省略して表記します。

プロパティ

PuyoMatchGameクラスで定義されているプロパティを示します。

コンストラクタ

終了処理

ゲームが終わったらそれ以上の処理が行なわれないようにイベントハンドラをRemoveします。

ゲーム開始時の処理

Field.Initメソッドでフィールドを初期化して各プロパティを初期化します。そして組ぷよを自然落下させるためのタイマーをStartさせます。

キー操作時の処理

キー操作時の処理を示します。引数でどちらのプレイヤーによる操作なのかを区別しています。

キーが押されたらフラグをセットしてキーが押されているあいだ処理を繰り返します。キーが離されたときは外部からフラグをクリアする処理がおこなわれます。またキー操作をするとIgnoreTimerForPuyoDowns[n]フラグをセットします。これがtrueだと次のタイマーによる落下を1回分パスします。いつまでもキー操作で時間稼ぎができないように上限値(16回まで)を設定しています。

回転時の処理

ZキーやXキーが押下されたら回転の処理をおこないます。

Timer.Elapsedイベントが発生したら落下中の組ぷよを自然落下させます。このときにIgnoreTimerForPuyoDowns[n]フラグがセットされているときはなにもしません。この場合は1回分だけパスしたいのでIgnoreTimerForPuyoDowns[n]フラグは常にセットします。

データ更新時の処理

ぷよの位置が変更された場合などデータが更新されたときの処理を示します。

ぷよの状態をクライアントサイドにカンマ区切りの文字列として送信したいので、その文字列を生成する処理を最初に示します。

落下中のぷよについても同様に文字列を取得する処理を定義します。

前述のメソッドでそれぞれのフィールドに存在するぷよの状態を文字列に変換して、各プロパティに格納します。

ぷよが固定されたときの処理

ぷよが固定されたときはイベントを発生させるとともにIsDownKeyDowns[n]フラグをクリアするとともにMoveCounts[n]をリセットします。

連鎖発生時の処理

連鎖発生時はおじゃまぷよをどれだけ敵側に送り込めるか?その前に相殺はあるのかを調べます。

連鎖発生時の処理を示します。上記のフィールド変数を初期化します。

連鎖進行時の処理を示します。追加される点数の総計を計算して、仮に確定したおじゃまぷよの数に追加分があるなら追加します。相殺が発生しているなら相殺します。

連鎖終了時の処理を示します。

ここでは確定したおじゃまぷよを送り込みます。連鎖発生の確定していた予告ぷよの数と比較して増えていれば増分を送り込みます。

おじゃまぷよ落下時の処理

おじゃまぷよを落下させるときはイベントを発生させるとともに、落下した分だけ予告ぷよの数を減らします。

次のぷよの確定時の処理

落下中のぷよが固定されて次のぷよが確定したらクライアントサイドにぷよの種類を示す文字列を送信するのですが、その文字列を生成する処理を先に示します。

つぎのぷよが確定したら、SendNextイベントを発生させ、引数として上記メソッドで生成した文字列を送ります。

ゲームオーバー時の処理

片方がゲームオーバーになったらそのプレイヤーの負けです。IsPlayingフラグをクリアしてタイマーを停止します。LostGameイベントを発生させます。