練習 - 遊戲邏輯
在此練習中,我們會將遊戲邏輯新增至應用程式,以確保最終遊戲運作正常。
為了協助讓本教學課程與 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
區塊內,新增呼叫ResetBoard
的方法OnInitialized
,如下所示:@code { protected override void OnInitialized() { State.ResetBoard(); } }
第一次向使用者顯示棋盤時,狀態會重設為遊戲的開頭。
建立遊戲棋子
接下來,讓我們配置可能遊玩的 42 個遊戲棋子。 我們可以將遊戲棋子表示為棋盤上 42 個 HTML 元素所參考的陣列。 我們可以藉由指派一組具有資料行和資料列位置的 CSS 類別來移動和放置這些棋子。
為了保存遊戲棋子,我們會在程式碼區塊中定義字串陣列欄位:
private string[] pieces = new string[42];
將程式碼新增至 HTML 區段,該區段會在相同的元件中為每個遊戲棋子建立 42
span
個標記:@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
的棋子,並擷取進入該棋子的資料列。 - 然後,我們可以定義三個 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
屬性,將互動式轉譯模式套用至元件。更新
Home
頁面上的Board
元件,以便其使用InteractiveServer
轉譯模式。<Board @rendermode="InteractiveServer" />
InteractiveServer
轉譯模式會透過瀏覽器的 WebSocket 連線處理伺服器的元件 UI 事件。使用這些變更執行應用程式。 現在看起來應該像這樣:
甚至更棒的是,當我們選取頂端的其中一個放置按鈕時,可以觀察到下列行為:
很棒! 我們現在可以將棋子新增至棋盤。 GameState
物件很聰明,足以在兩個玩家之間來回切換。 繼續並選取更多下拉式按鈕,並觀看結果。
勝出及錯誤處理
如果您以目前的設定玩遊戲,當您嘗試將太多棋子放在相同的棋格中,以及一個玩家贏得遊戲時,它就會引發錯誤。
讓我們將一些錯誤處理和指標新增至棋盤,讓遊戲的目前狀態更清楚。 在棋盤上方和放置按鈕下方新增狀態區域。
在關閉
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>
這個標記可讓我們顯示下列項目的指標:
- 宣佈遊戲獲勝者
- 可讓我們重新啟動遊戲的按鈕
- 錯誤訊息
- 目前玩家的回合
現在讓我們填入一些設定這些值的邏輯。
在棋子陣列後新增下列程式碼:
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
,我們會在畫面上顯示重設按鈕。
讓我們在玩棋子時處理錯誤訊息。 新增一行以清除錯誤訊息,然後將
PlayPiece
方法中的程式碼以try...catch
區塊包裝以設定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; }
我們的錯誤處理常式指標很簡單,並使用 Bootstrap CSS 架構在危險模式中顯示錯誤。
接下來,讓我們新增
ResetGame
按鈕觸發以重新啟動遊戲的方法。 目前,重新開始遊戲的唯一方法是重新整理頁面。 此程式碼可讓我們停留在相同的頁面上。void ResetGame() { State.ResetBoard(); winnerMessage = string.Empty; errorMessage = string.Empty; pieces = new string[42]; }
現在我們的
ResetGame
方法具有下列邏輯:- 重設棋盤的狀態。
- 隱藏我們的指標。
- 將棋子陣列重設為 42 個字串的空陣列。
此更新應該能讓我們再次玩遊戲,現在我們會看到一個指標正上方宣告玩家回合,最後是遊戲完成。
我們仍存在無法選取重設按鈕的情況。 讓我們在
PlayPiece
方法中新增一些邏輯,以偵測遊戲結束。讓我們藉由在
PlayPiece
中的try...catch
區塊後,新增切換運算式,來偵測遊戲是否有優勝者。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
會傳回列舉,如果有任何玩家贏得遊戲,或遊戲平手,則會傳回列舉。 如果發生遊戲結束狀態,這個切換運算式將會適當地設定winnerMessage
欄位。現在當我們遊玩並到達遊戲結束案例時,這些指標會隨即出現:
摘要
我們已深入了解 Blazor,並建置一個可愛的小遊戲。 以下是我們學到的幾個技能:
- 已建立元件
- 將該元件新增至首頁
- 已使用相依性插入來管理遊戲的狀態
- 讓遊戲與事件處理常式互動,以放置棋子並重設遊戲
- 撰寫錯誤處理常式來報告遊戲的狀態
- 已將參數新增至我們的元件
我們建置的專案是簡單的遊戲,而且有許多您可以執行的項目。 在尋找改善它的挑戰嗎?
挑戰
請考慮下列挑戰:
- 若要讓應用程式更小,請移除預設版面配置和額外的頁面。
- 改善
Board
元件的參數,讓您可以傳遞任何有效的 CSS 色彩值。 - 使用一些 CSS 和 HTML 版面配置改善指標外觀。
- 引進音效。
- 新增視覺指標,並防止在棋格已滿時使用放置按鈕。
- 新增網路功能,讓您可以在朋友的瀏覽器中一起遊玩。
- 使用 Blazor 應用程式將遊戲插入 .NET MAUI,並在手機或平板電腦上玩遊戲。
享受撰寫程式碼的樂趣吧!