ASP.NET Core版 対人対戦できるぷよぷよをつくる(3)の続きです。これまでは1回で勝負が決まっていましたが、今回は3本先取に書き換えます。
どこを書き換えるか? 要はゲームが終了したときにそのまま終わりにしなければよいのです。
勝ち数を格納する配列を定義する
まずPuyoMatchGameクラスに勝ち数を格納する配列を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
namespace PuyoMatch { public class PuyoMatchGame { public const int WIN_COUNT_MAX = 3; // コンストラクタ内で WinCounts = new int[2];と初期化しておく public int[] WinCounts { get; } } } |
PuyoMatchHubクラスの修正
そしてPuyoMatchHubクラスの勝敗が決したときのイベントハンドラを変更します。PuyoMatchHubクラスはAspNetCore.SignalRにおける処理 ASP.NET Core版 対人対戦できるぷよぷよをつくる(2)を参照してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
namespace PuyoMatch { public class PuyoMatchHub : Hub { private async void Game_LostGame(PuyoMatchGame game, bool isPlayer) { string winnerId; string loserId; string escapedWinnerName; string escapedLoserName; if (isPlayer) { game.WinCounts[1]++; // 勝った側の勝ち数をインクリメントする winnerId = game.ConnectionIds[1]; loserId = game.ConnectionIds[0]; escapedWinnerName = game.EscapedPlayerNames[1]; escapedLoserName = game.EscapedPlayerNames[0]; } else { game.WinCounts[0]++; winnerId = game.ConnectionIds[0]; loserId = game.ConnectionIds[1]; escapedWinnerName = game.EscapedPlayerNames[0]; escapedLoserName = game.EscapedPlayerNames[1]; } Task[] tasks1 = new Task[4]; if (ClientProxyMap.ContainsKey(winnerId)) { tasks1[0] = ClientProxyMap[winnerId].SendAsync("WonThisGameToClient"); string str1 = $"あなたは {YellowTag}{escapedLoserName}{SpanTagEnd} さんに{RedTag}勝ち{SpanTagEnd}ました。"; tasks1[1] = ClientProxyMap[winnerId].SendAsync("NotifyToClient", str1); } if (ClientProxyMap.ContainsKey(loserId)) { tasks1[2] = ClientProxyMap[loserId].SendAsync("LostThisGameToClient"); string str2 = $"あなたは {YellowTag}{escapedWinnerName}{SpanTagEnd} さんに{RedTag}負け{SpanTagEnd}ました。"; tasks1[3] = ClientProxyMap[loserId].SendAsync("NotifyToClient", str2); } Task.WaitAll(tasks1); // 処理を追加したのはここから // 3本先取なのでどちらの勝ち数も3に満たない場合は次の対戦が開始される // 片方が3勝している場合は終了 if (game.WinCounts[0] >= PuyoMatchGame.WIN_COUNT_MAX || game.WinCounts[1] >= PuyoMatchGame.WIN_COUNT_MAX) { game.Uninitialize(); RemoveEventHandlers(game); await ClientProxyMap[winnerId].SendAsync("FinishedGameToClient"); await ClientProxyMap[loserId].SendAsync("FinishedGameToClient"); try { if (!Games.Remove(game)) return; foreach (IClientProxy client in ClientProxyMap.Values) await client.SendAsync("GameCountChangedToClient", Games.Count); } catch { Console.WriteLine("例外発生"); return; } return; } // どちらも3勝していない場合は3秒後に次の対戦を再開する await Task.Delay(3000); game.NextGameStart(true); int gameCount = game.WinCounts[0] + game.WinCounts[1] + 1; if (ClientProxyMap.ContainsKey(winnerId)) { tasks1[0] = SendMatchString(winnerId, escapedWinnerName, escapedLoserName, gameCount); tasks1[1] = ClientProxyMap[winnerId].SendAsync("NextGameStartedToClient"); } if (ClientProxyMap.ContainsKey(loserId)) { tasks1[2] = SendMatchString(loserId, escapedLoserName, escapedWinnerName, gameCount); tasks1[3] = ClientProxyMap[loserId].SendAsync("NextGameStartedToClient"); } Task.WaitAll(tasks1); CheckGameAbandoned(game.ConnectionIds, game.EscapedPlayerNames); } } } |
SendMatchStringメソッドは対戦しているときに、いま第何戦なのかがわかるようにするだけのものでたいした意味はありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
namespace PuyoMatch { public class PuyoMatchHub : Hub { async Task SendMatchString(string id, string escapedMyName, string escapedOpponentName, int gameCount) { if (ClientProxyMap.ContainsKey(id)) { string str = $"{YellowTag}{PuyoMatchGame.WIN_COUNT_MAX}{SpanTagEnd} 本先取:第 {RedTag}{gameCount}{SpanTagEnd} 戦目<br>"; str += $"{YellowTag}{escapedMyName}{SpanTagEnd} さんは "; str += $"{RedTag}{escapedOpponentName}{SpanTagEnd} さんと対戦中です。"; await ClientProxyMap[id].SendAsync("NotifyToClient", str); } } } } |
クライアントサイドにおける処理
これまでは対戦がおわったらクライアントサイドに”WonGameToClient”または”LostGameToClient”を送信していましたが、これからは”WonThisGameToClient”または”LostThisGameToClient”を送信します。どちらかが3勝したら”FinishedGameToClient”を送信します。そうでない場合は次のゲームが再開され、そのときは”NextGameStartedToClient”が送信されます。
クライアントサイドではこれらを送信したら以下の処理をおこないます。
新しくisStoppingPlayというグローバル変数を定義しています。これはプレイは続行中ではあるけれども中断されていることを示すフラグです。このフラグがセットされているあいだはBGMの再生が中断されます。
wwwroot\puyo-match\puyo-match.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let isStoppingPlay = false; connection.on("LostThisGameToClient", function () { if (IsSound()) { lostSound.currentTime = 0; lostSound.play(); } isStoppingPlay = true; StopBgm(); }); connection.on("WonThisGameToClient", function () { if (IsSound()) { winSound.currentTime = 0; winSound.play(); } isStoppingPlay = true; StopBgm(); }); |
新しいフラグが追加されたのでIsSound関数が少しだけ変更になります。
wwwroot\puyo-match\puyo-match.js
1 2 3 |
function IsSound() { return isPlaying && document.getElementById('sound-checkbox').checked && !isStoppingPlay; } |
またクライアントサイドで”NextGameStartedToClient”を受信したときは以下の処理がおこなわれます。
wwwroot\puyo-match\puyo-match.js
1 2 3 |
connection.on("NextGameStartedToClient", function () { isStoppingPlay = false; }); |
どちらかが3勝した場合はゲーム終了です。この場合はサーバーサイドから”FinishedGameToClient”が送られてくるのでエントリーするためのボタンを再表示させます。
wwwroot\puyo-match\puyo-match.js
1 2 3 4 5 6 7 |
connection.on("FinishedGameToClient", function () { isPlaying = false; isStoppingPlay = false; entery.style.display = 'block'; enteryButton.value = 'もう一度エントリーする'; }); |
双方の勝ち数を表示させる
また対戦中は自分と相手の勝ち数が表示されたほうがわかりやすいと思うので表示させます。
これはサーバーサイドの処理です。変更したのは一部だけです。”WinCountsToClient”を送信する部分を追加しただけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
namespace PuyoMatch { public class PuyoMatchHub : Hub { void Update(PuyoMatchGame game) { if (!game.IsPlaying) return; IClientProxy?[] clients = GetClients(game); foreach (IClientProxy? client in clients) { if (client == null) continue; Task.Run(async () => { await client.SendAsync("BeginUpdatedToClient"); await client.SendAsync( "FixedPuyoUpdatedToClient", true, game.FixedPuyoRowsTexts[0], game.FixedPuyoColsTexts[0], game.FixedPuyoTypesTexts[0], game.FixedPuyoRensasTexts[0] ); await client.SendAsync( "FixedPuyoUpdatedToClient", false, game.FixedPuyoRowsTexts[1], game.FixedPuyoColsTexts[1], game.FixedPuyoTypesTexts[1], game.FixedPuyoRensasTexts[1] ); await client.SendAsync( "FallingPuyoUpdatedToClient", true, game.FallingPuyoRowsTexts[0], game.FallingPuyoColsTexts[0], game.FallingPuyoTypesTexts[0] ); await client.SendAsync( "FallingPuyoUpdatedToClient", false, game.FallingPuyoRowsTexts[1], game.FallingPuyoColsTexts[1], game.FallingPuyoTypesTexts[1] ); await client.SendAsync("ScoreUpdatedToClient", true, game.Scores[0], game.OjamaCounts[0], game.PlayerNames[0]); await client.SendAsync("ScoreUpdatedToClient", false, game.Scores[1], game.OjamaCounts[1], game.PlayerNames[1]); await client.SendAsync("PingUpdatedToClient", true, game.Pings[0]); await client.SendAsync("PingUpdatedToClient", false, game.Pings[1]); await client.SendAsync("WinCountsToClient", true, game.WinCounts[0]); await client.SendAsync("WinCountsToClient", false, game.WinCounts[1]); await client.SendAsync("EndUpdatedToClient"); }); } } } } |
クライアントサイドでは”WinCountsToClient”を受信したときの処理を追加します。
wwwroot\puyo-match\puyo-match.js
1 2 3 4 5 6 7 8 9 |
let winCount0 = 0; let winCount1 = 0; connection.on("WinCountsToClient", function (isFirstPlayer, winCount) { if ((isFirstPlayer && isThisFirstPlayer) || (!isFirstPlayer && !isThisFirstPlayer)) winCount0 = winCount; else winCount1 = winCount; }); |
それから描画処理をおこなうところで勝ち数を表示させます。長々と書いていますが、2戦目以降は勝ち数を表示させる処理を追加しているだけです。
wwwroot\puyo-match\puyo-match.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
function DrawScore(isFirstPlayer) { let marginLeft; let marginTop; let score; let yokoku; let offsetCount; let playerName; let ping; let winCount; // これを追加した if ((isFirstPlayer && isThisFirstPlayer) || (!isFirstPlayer && !isThisFirstPlayer)) { marginLeft = marginLeftPlayer1; marginTop = marginTopPlayer1; score = score0; yokoku = yokoku0; offsetCount = offsetCount0; playerName = playerName0; ping = ping0; winCount = winCount0; } else { marginLeft = marginLeftPlayer2; marginTop = marginTopPlayer2; score = score1; yokoku = yokoku1; offsetCount = offsetCount1; playerName = playerName1; ping = ping1; winCount = winCount1; } // 2戦目以降は勝ち数を表示させる ctx.font = 'bold 16px MS ゴシック'; if(winCount0 == 0 && winCount1 == 0){ ctx.fillStyle = '#ff0'; ctx.fillText(playerName, marginLeft + 20, marginTop + 0); } else { ctx.fillStyle = '#f40'; ctx.fillText(`<${winCount}勝>`, marginLeft + 20, marginTop + 0); ctx.fillStyle = '#ff0'; ctx.fillText(playerName, marginLeft + 70, marginTop + 0); } ctx.fillStyle = '#0ff'; ctx.font = 'bold 32px MS ゴシック'; ctx.fillText(score, marginLeft + 20, marginTop + 40); ctx.fillStyle = '#f40'; ctx.font = 'bold 20px MS ゴシック'; let text = '(' + yokoku + ')'; ctx.fillText(text, marginLeft + 160, marginTop + 30); if(offsetCount != 0) { let offsetText = '(相殺:' + offsetCount + ')'; ctx.fillStyle = '#ff0'; ctx.font = 'bold 20px MS ゴシック'; ctx.fillText(offsetText, marginLeft + 160, marginTop + 55); } ctx.fillStyle = '#fff'; ctx.font = 'bold 16px MS ゴシック'; ctx.fillText(`ping = ${ping} ms`, marginLeft + 120, marginTop + 440); ctx.fillStyle = '#000'; } |