演習 - ゲーム ロジック
この演習では、アプリにゲーム ロジックを追加して、フルに機能するゲームを完成させます。
Blazor について説明するというトピックに沿ってこのチュートリアルを進めるために、ゲームを管理するためのロジックを含む GameState
というクラスを提供します。
ゲームの状態の追加
GameState
クラスをプロジェクトに追加し、依存関係の挿入を通じてシングルトン サービスとしてコンポーネントで使用できるようにしましょう。
GameState.cs ファイルをプロジェクトのルートにコピーします。
プロジェクトのルートにある Program.cs ファイルを開き、次のステートメントを追加して
GameState
をアプリ内のシングルトン サービスとして構成します。builder.Services.AddSingleton<GameState>();
これで、
GameState
クラスのインスタンスをBoard
コンポーネントに挿入できるようになりました。Board.razor ファイルの先頭に次の
@inject
ディレクティブを追加します。 次のディレクティブは、ゲームの現在の状態をコンポーネントに挿入します。@inject GameState State
Board
コンポーネントをゲームの状態に接続できるようになりました。
状態のリセット
まず、Board
コンポーネントが最初に画面に描画されたときのゲームの状態をリセットしましょう。 コンポーネントの初期化時にゲームの状態をリセットするコードをいくつか追加します。
次のように、Board.razor ファイルの下部にある
@code
ブロック内に、OnInitialized
メソッドを追加してResetBoard
を呼び出します。@code { protected override void OnInitialized() { State.ResetBoard(); } }
ボードが最初にユーザーに表示されると、状態はゲームの開始時にリセットされます。
ゲーム ピースの作成
次に、配置可能な 42 個のゲーム ピースを割り当ててみましょう。 ゲーム ピースは、ボード上の 42 個の HTML 要素によって参照される配列として表すことができます。 列と行の位置を持つ CSS クラスのセットを割り当てることで、これらのピースを移動して配置できます。
ゲーム ピースを保持するために、以下のようにコード ブロック内で文字列配列フィールドを定義します。
private string[] pieces = new string[42];
同じコンポーネントの HTML セクションに、42 個の
span
タグを作成するコードをゲーム ピースごとに 1 つずつ追加します。@for (var i = 0; i < 42; i++) { <span class="@pieces[i]"></span> }
完全なコードは次のようになります。
<div> <div class="board"> @for (var i = 0; i < 42; i++) { <span class="container"> <span></span> </span> } </div> @for (var i = 0; i < 42; i++) { <span class="@pieces[i]"></span> } </div> @code { private string[] pieces = new string[42]; protected override void OnInitialized() { State.ResetBoard(); } }
これにより、各ゲーム ピース スパンの CSS クラスに空の文字列が割り当てられます。 CSS クラスが空の文字列の場合、スタイルが適用されないので、ゲーム ピースは画面に表示されません。
ゲーム ピースの配置の処理
プレーヤーが列にピースを配置するときに処理するメソッドを追加してみましょう。 GameState
クラスでは、ゲーム ピースに正しい行を割り当てる方法が認識されており、そのピースが着地した行が報告されます。 この情報を使用して、プレーヤーの色、ピースの最終的な場所、および CSS ドロップ アニメーションを表す CSS クラスを割り当てることができます。
このメソッドには PlayPiece
という名前を付けます。これはプレーヤーが選択した列を指定する入力パラメーターを受け取ります。
前の手順で定義した
pieces
配列の下に、このコードを追加します。private void PlayPiece(byte col) { var player = State.PlayerTurn; var turn = State.CurrentTurn; var landingRow = State.PlayPiece(col); pieces[turn] = $"player{player} col{col} drop{landingRow}"; }
PlayPiece
コードの動作を次に示します。
- 送信された
col
という名前の列にピースを配置し、そのピースが着地した行をキャプチャするように、ゲームの状態に指示します。 - 次に、ゲーム ピースに割り当てる 3 つの CSS クラスを定義して、現在動作しているプレーヤー、ピースが配置された列、ピースが着地した行を識別できます。
- メソッドの最後の行では、これらのクラスが
pieces
配列内のそのゲーム ピースに割り当てられます。
提供された Board.razor.css を見ると、列、行、プレーヤー ターンに一致する CSS クラスが存在することがわかります。
その結果生じる効果として、このメソッドが呼び出されると、ゲーム ピースが列に配置され、最下部の行まで落下するようにアニメーション化されます。
列の選択
次に、プレーヤーが列を選択して新しい PlayPiece
メソッドを呼び出ることができるように、いくつかのコントロールを配置する必要があります。 この列にピースを落とすことができることを示すために "🔽" という文字を使用します。
先頭の
<div>
タグの上に、クリック可能なボタンの行を追加します。<nav> @for (byte i = 0; i < 7; i++) { var col = i; <span title="Click to play a piece" @onclick="() => PlayPiece(col)">🔽</span> } </nav>
@onclick
属性では、クリック イベントのイベント ハンドラーを指定します。 しかし、UI イベントを処理するには、''対話型レンダリング モード'' を使用して Blazor コンポーネントをレンダリングする必要があります。 既定では、Blazor コンポーネントはサーバーから静的にレンダリングされます。@rendermode
属性を使用して、コンポーネントに対話型レンダリング モードを適用できます。InteractiveServer
レンダー モードを使用するように、Home
ページ上のBoard
コンポーネントを更新します。<Board @rendermode="InteractiveServer" />
InteractiveServer
レンダー モードでは、ブラウザーとの WebSocket 接続を介してサーバーからコンポーネントの UI イベントを処理します。これらの変更を使用してアプリを実行します。 このようになるはずです。
より良い方法として、最上部にある下矢印ボタンの 1 つを選択すると、次の動作を実現できます。
大きな進歩です。 ボードにピースを追加できるようになりました。 GameState
オブジェクトは、2 人のプレーヤー間を行き来できるので有用です。 その他の下矢印ボタンを選択して、結果を確認します。
勝利とエラー処理
現在の構成でゲームをプレイすると、同じ列に多すぎるピースを入れようとしたときと、一方のプレイヤーがゲームに勝利したときにエラーが発生することがわかります。
いくつかのエラー処理とインジケーターをボードに追加することで、ゲームの現在の状態を明確にしましょう。 ボードの上かつ落下ボタンの下に、ステータス領域を追加します。
nav
要素の後に、次のマークアップを挿入します。<article> @winnerMessage <button style="@ResetStyle" @onclick="ResetGame">Reset the game</button> <br /> <span class="alert-danger">@errorMessage</span> <span class="alert-info">@CurrentTurn</span> </article>
このマークアップを使用すると、次のインジケーターを表示できます。
- ゲームの勝者の発表
- ゲームを再起動できるボタン
- エラー メッセージ
- 現在のプレーヤーのターン
次に、これらの値を設定するいくつかのロジックを入力しましょう。
pieces 配列の後に次のコードを追加します。
private string[] pieces = new string[42]; private string winnerMessage = string.Empty; private string errorMessage = string.Empty; private string CurrentTurn => (winnerMessage == string.Empty) ? $"Player {State.PlayerTurn}'s Turn" : ""; private string ResetStyle => (winnerMessage == string.Empty) ? "display: none;" : "";
CurrentTurn
プロパティは、winnerMessage
の状態とGameState
のPlayerTurn
プロパティに基づいて自動的に計算されます。ResetStyle
はWinnerMessage
の内容に基づいて計算されます。winnerMessage
がある場合、リセット ボタンが画面に表示されるようにします。
ピースの配置時にエラー メッセージを処理してみましょう。 エラー メッセージをクリアする行を追加し、
try...catch
ブロックでPlayPiece
メソッドのコードをラップして、例外が発生した場合のerrorMessage
を設定します。errorMessage = string.Empty; try { var player = State.PlayerTurn; var turn = State.CurrentTurn; var landingRow = State.PlayPiece(col); pieces[turn] = $"player{player} col{col} drop{landingRow}"; } catch (ArgumentException ex) { errorMessage = ex.Message; }
エラー ハンドラー インジケーターは単純で、ブートストラップ CSS フレームワークを使用して危険モードでエラーが表示されます。
次に、ゲームを再起動するためにボタンでトリガーする
ResetGame
メソッドを追加してみましょう。 現在、ゲームを再起動する唯一の方法は、ページを更新することです。 このコードを使用すると、同じページを維持できます。void ResetGame() { State.ResetBoard(); winnerMessage = string.Empty; errorMessage = string.Empty; pieces = new string[42]; }
これで、
ResetGame
メソッドに次のロジックが追加されました。- ボードの状態をリセットします。
- インジケーターを非表示にします。
- pieces 配列を 42 個の文字列の空の配列にリセットします。
この更新プログラムでは、再びゲームをプレイできるようにする必要があります。プレーヤーのターンを宣言し、最終的にゲームの完了を宣言するインジケータが、ボードのすぐ上に表示されています。
リセット ボタンを選択することができないという問題がまだ存在します。
PlayPiece
メソッド内にゲームの終了を検出するロジックをいくつか追加してみましょう。PlayPiece
のtry...catch
ブロックの後に switch 式を追加して、ゲームに勝者がいるかどうかを検出してみましょう。winnerMessage = State.CheckForWin() switch { GameState.WinState.Player1_Wins => "Player 1 Wins!", GameState.WinState.Player2_Wins => "Player 2 Wins!", GameState.WinState.Tie => "It's a tie!", _ => "" };
CheckForWin
メソッドは、どのプレーヤーがゲームに勝ったか (勝者がいる場合)、またはゲームが引き分けであるかを報告する列挙型を返します。 この switch 式は、ゲーム オーバー状態が発生した場合にwinnerMessage
フィールドを適切に設定します。ゲームをプレイしてゲーム終了シナリオに到達すると、次のインジケーターが表示されます。
まとめ
ここでは Blazor について多くのことを学び、すっきりとした小さなゲームを構築しました。 学習したスキルは次のとおりです。
- コンポーネントを作成しました
- そのコンポーネントをホーム ページに追加しました
- 依存関係の挿入を使用してゲームの状態を管理しました
- ピースを配置してゲームをリセットするイベント ハンドラーを使用して、ゲームをインタラクティブにしました
- ゲームの状態を報告するエラー ハンドラーを記述しました
- コンポーネントにパラメーターを追加しました
私たちが構築したプロジェクトは単純なゲームであり、それを使ってできることはたくさんあります。 それを改善するために何か課題を求めていますか?
課題
次の課題を考えてみましょう。
- このアプリをより小さくするために、既定のレイアウトと余分なページを削除します。
- 任意の有効な CSS カラー値を渡すことができるように、
Board
コンポーネントのパラメーターを改善します。 - CSS や HTML レイアウトを使用してインジケーターの外観を改善します。
- 効果音を導入します。
- 視覚インジケーターを追加し、列がいっぱいになったときに下矢印ボタンが使用されないようにします。
- ブラウザーで友達とプレイできるように、ネットワーク機能を追加します。
- Blazor アプリケーションを使用して .NET MAUI にゲームを挿入し、携帯電話やタブレットでプレイする。
コーディングをお楽しみください。