練習 - 遊戲邏輯

已完成

在此練習中,我們會將遊戲邏輯新增至應用程式,以確保最終遊戲運作正常。

為了協助讓本教學課程與 Blazor 相關教學,我們提供一個名為 GameState 的類別,其中包含管理遊戲的邏輯。

新增遊戲狀態

讓我們將 GameState 類別新增至您的專案,然後透過相依性插入將其以單一服務的形式提供給元件。

  1. GameState.cs 檔案複製到專案的根目錄中。

  2. 在專案的根目錄開啟 Program.cs 檔案,並新增此陳述式,以在應用程式中將 GameState 設定為單一服務:

    builder.Services.AddSingleton<GameState>();
    

    我們現在可以將 GameState 類別的執行個體插入 Board 元件中。

  3. Board.razor 檔案頂端新增下列 @inject 指示詞。 指示字會將遊戲的目前狀態插入元件:

    @inject GameState State
    

    我們現在可以開始將 Board 元件連線到遊戲的狀態。

重設狀態

讓我們從第一次在畫面上繪製 Board 元件時重設遊戲的狀態開始。 新增一些程式碼,以在元件初始化時重設遊戲的狀態。

  1. Board.razor 檔案底部的 @code 區塊內,新增呼叫 ResetBoard 的方法 OnInitialized,如下所示:

    @code {
        protected override void OnInitialized()
        {
            State.ResetBoard();
        }
    }
    

    第一次向使用者顯示棋盤時,狀態會重設為遊戲的開頭。

建立遊戲棋子

接下來,讓我們配置可能遊玩的 42 個遊戲棋子。 我們可以將遊戲棋子表示為棋盤上 42 個 HTML 元素所參考的陣列。 我們可以藉由指派一組具有資料行和資料列位置的 CSS 類別來移動和放置這些棋子。

  1. 為了保存遊戲棋子,我們會在程式碼區塊中定義字串陣列欄位:

    private string[] pieces = new string[42];
    
  2. 將程式碼新增至 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,並接受輸入參數,指定玩家所選擇的棋格。

  1. 在上一個步驟中定義的 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 程式碼的功能:

  1. 我們會告訴遊戲狀態在提交的資料行中玩名為 col 的棋子,並擷取進入該棋子的資料列。
  2. 然後,我們可以定義三個 CSS 類別,以指派給遊戲棋子,以識別目前作用中的玩家、放置棋子的棋格,以及所在的列數。
  3. 方法的最後一行會將這些類別指派給 pieces 陣列中的遊戲棋子。

如果您在提供的 Board.razor.css 中尋找,您會找到符合的棋格、列數和玩家回合的 CSS 類別。

結果效果是遊戲棋子放在資料行中,並在呼叫此方法時以動畫方式放入最下方的資料列。

選擇資料行

接下來,我們需要放置一些控制項,讓玩家選擇資料行並呼叫我們的新 PlayPiece 方法。 我們會使用「🔽」字元來指出您可以在此棋格中放置棋子。

  1. 在起始 <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 屬性,將互動式轉譯模式套用至元件。

  2. 更新 Home 頁面上的 Board 元件,以便其使用 InteractiveServer 轉譯模式。

    <Board @rendermode="InteractiveServer" />
    

    InteractiveServer 轉譯模式會透過瀏覽器的 WebSocket 連線處理伺服器的元件 UI 事件。

  3. 使用這些變更執行應用程式。 現在看起來應該像這樣:

    四子棋棋盤的螢幕擷取畫面。

    甚至更棒的是,當我們選取頂端的其中一個放置按鈕時,可以觀察到下列行為:

    四子棋動畫的螢幕擷取畫面。

很棒! 我們現在可以將棋子新增至棋盤。 GameState 物件很聰明,足以在兩個玩家之間來回切換。 繼續並選取更多下拉式按鈕,並觀看結果。

勝出及錯誤處理

如果您以目前的設定玩遊戲,當您嘗試將太多棋子放在相同的棋格中,以及一個玩家贏得遊戲時,它就會引發錯誤。

讓我們將一些錯誤處理和指標新增至棋盤,讓遊戲的目前狀態更清楚。 在棋盤上方和放置按鈕下方新增狀態區域。

  1. 在關閉 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>
    

    這個標記可讓我們顯示下列項目的指標:

    • 宣佈遊戲獲勝者
    • 可讓我們重新啟動遊戲的按鈕
    • 錯誤訊息
    • 目前玩家的回合

    現在讓我們填入一些設定這些值的邏輯。

  2. 在棋子陣列後新增下列程式碼:

    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 的狀態和 GameStatePlayerTurn 屬性自動計算。
    • ResetStyle 會根據 WinnerMessage 的內容來計算。 如果有 winnerMessage,我們會在畫面上顯示重設按鈕。
  3. 讓我們在玩棋子時處理錯誤訊息。 新增一行以清除錯誤訊息,然後將 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 架構在危險模式中顯示錯誤。

    您目前的遊戲螢幕擷取畫面,有棋盤和棋子。

  4. 接下來,讓我們新增 ResetGame 按鈕觸發以重新啟動遊戲的方法。 目前,重新開始遊戲的唯一方法是重新整理頁面。 此程式碼可讓我們停留在相同的頁面上。

    void ResetGame()
    {
        State.ResetBoard();
        winnerMessage = string.Empty;
        errorMessage = string.Empty;
        pieces = new string[42];
    }
    

    現在我們的 ResetGame 方法具有下列邏輯:

    • 重設棋盤的狀態。
    • 隱藏我們的指標。
    • 將棋子陣列重設為 42 個字串的空陣列。

    此更新應該能讓我們再次玩遊戲,現在我們會看到一個指標正上方宣告玩家回合,最後是遊戲完成。

    螢幕擷取畫面呈現遊戲結束。

    我們仍存在無法選取重設按鈕的情況。 讓我們在 PlayPiece 方法中新增一些邏輯,以偵測遊戲結束。

  5. 讓我們藉由在 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,並在手機或平板電腦上玩遊戲。

享受撰寫程式碼的樂趣吧!