Ćwiczenie — logika gry

Ukończone

W tym ćwiczeniu dodamy logikę gry do naszej aplikacji, aby zapewnić, że skończymy z w pełni działającą grą.

Aby ułatwić utrzymanie tego samouczka na temat nauczania na temat platformy Blazor, udostępniamy klasę o nazwie GameState zawierającą logikę zarządzania grą.

Dodawanie stanu gry

Dodajmy klasę GameState do projektu, a następnie udostępnimy ją składnikom jako pojedynczą usługę poprzez wstrzyknięcie zależności.

  1. Skopiuj plik GameState.cs do katalogu głównego projektu.

  2. Otwórz plik Program.cs w katalogu głównym projektu i dodaj tę instrukcję, która konfiguruje GameState jako pojedynczą usługę w aplikacji:

    builder.Services.AddSingleton<GameState>();
    

    Teraz możemy wstrzyknąć wystąpienie GameState klasy do naszego Board składnika.

  3. Dodaj następującą @inject dyrektywę w górnej części pliku Board.razor . dyrektywa wprowadza bieżący stan gry do składnika:

    @inject GameState State
    

    Możemy teraz zacząć łączyć nasz Board składnik ze stanem gry.

Stan resetowania

Zacznijmy od zresetowania stanu gry, gdy Board składnik jest po raz pierwszy malowany na ekranie. Dodaj kod, aby zresetować stan gry po zainicjowaniu składnika.

  1. Dodaj metodę OnInitialized z wywołaniem metody do ResetBoard, wewnątrz @code bloku w dolnej części pliku Board.razor , w następujący sposób:

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

    Gdy tablica zostanie po raz pierwszy wyświetlona użytkownikowi, stan zostanie zresetowany do początku gry.

Tworzenie elementów gry

Następnie przydzielmy możliwe 42 sztuk gry, które mogą być odtwarzane. Możemy reprezentować elementy gry jako tablicę, do których odwołuje się 42 elementy HTML na tablicy. Możemy przenosić i umieszczać te elementy, przypisując zestaw klas CSS z pozycjami kolumn i wierszy.

  1. Aby przechowywać nasze elementy gry, definiujemy pole tablicy ciągów w bloku kodu:

    private string[] pieces = new string[42];
    
  2. Dodaj kod do sekcji HTML, która tworzy 42 span tagi, po jednym dla każdego elementu gry, w tym samym składniku:

    @for (var i = 0; i < 42; i++)
    {
       <span class="@pieces[i]"></span>
    }
    

    Pełny kod powinien wyglądać następująco:

    <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();
        }
    }
    

    Spowoduje to przypisanie pustego ciągu do klasy CSS każdego fragmentu gry. Pusty ciąg klasy CSS uniemożliwia pojawianie się elementów gry na ekranie, ponieważ nie zastosowano do nich żadnego stylu.

Obsługa umieszczania elementów gry

Dodajmy metodę do obsługi, gdy gracz umieszcza fragment w kolumnie. Klasa GameState wie, jak przypisać prawidłowy wiersz do elementu gry, i zgłasza wiersz z powrotem, w którym się ląduje. Możemy użyć tych informacji, aby przypisać klasy CSS reprezentujące kolor odtwarzacza, ostateczną lokalizację elementu i animację upuszczania CSS.

Wywołujemy tę metodę PlayPiecei akceptuje parametr wejściowy określający kolumnę wybraną przez odtwarzacz.

  1. Dodaj ten kod poniżej tablicy pieces zdefiniowanej w poprzednim kroku.

    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}";
    }
    

Oto co PlayPiece robi kod:

  1. Mówimy stanowi gry, aby zagrać kawałek w przesłanej kolumnie o nazwie col i przechwycić wiersz, w którym wylądował kawałek.
  2. Następnie możemy zdefiniować trzy klasy CSS, które mają zostać przypisane do elementu gry, aby zidentyfikować, który gracz działa obecnie, kolumnę, w której umieszczono kawałek, oraz wiersz docelowy.
  3. Ostatni wiersz metody przypisuje te klasy do tego elementu gry w tablicy pieces .

Jeśli przyjrzysz się podanej Board.razor.css, znajdziesz klasy CSS pasujące do kolumny, wiersza i kolei odtwarzacza.

Wynikowy efekt polega na tym, że element gry jest umieszczony w kolumnie i animowany, aby upuść do najbardziej dolnego wiersza, gdy ta metoda jest wywoływana.

Wybieranie kolumny

Następnie musimy umieścić pewne kontrolki, które pozwalają graczom wybrać kolumnę i wywołać naszą nową PlayPiece metodę. Używamy znaku "🔽", aby wskazać, że można usunąć fragment w tej kolumnie.

  1. Nad tagiem początkowym <div> dodaj wiersz przycisków możliwych do kliknięcia:

    <nav>
        @for (byte i = 0; i < 7; i++)
        {
            var col = i;
            <span title="Click to play a piece" @onclick="() => PlayPiece(col)">🔽</span>
        }
    </nav>
    

    Atrybut @onclick określa procedurę obsługi zdarzeń dla zdarzenia kliknięcia. Jednak aby obsługiwać zdarzenia interfejsu użytkownika, składnik Blazor musi być renderowany przy użyciu interaktywnego trybu renderowania. Domyślnie składniki platformy Blazor są renderowane statycznie z serwera. Możemy zastosować tryb renderowania interakcyjnego do składnika przy użyciu atrybutu @rendermode .

  2. Board Zaktualizuj składnik na Home stronie, aby używał trybu renderowaniaInteractiveServer.

    <Board @rendermode="InteractiveServer" />
    

    Tryb renderowania InteractiveServer obsługuje zdarzenia interfejsu użytkownika dla składników z serwera za pośrednictwem połączenia protokołu WebSocket z przeglądarką.

  3. Uruchom aplikację z tymi zmianami. Teraz powinno to wyglądać następująco:

    Zrzut ekranu przedstawiający tablicę Połącz cztery.

    Jeszcze lepiej, gdy wybierzemy jeden z przycisków upuszczania u góry, można zaobserwować następujące zachowanie:

    Zrzut ekranu przedstawiający animację Połącz cztery.

Świetny postęp! Teraz możemy dodać elementy do tablicy. Obiekt GameState jest wystarczająco inteligentny, aby przewróć się tam iz powrotem między dwoma graczami. Przejdź dalej i wybierz więcej przycisków upuszczania i obejrzyj wyniki.

Zwycięska obsługa błędów

Jeśli grasz w grę w bieżącej konfiguracji, okaże się, że zgłasza błędy, gdy próbujesz umieścić zbyt wiele sztuk w tej samej kolumnie i kiedy jeden gracz wygra grę.

Wyjaśnijmy bieżący stan naszej gry, dodając pewne wskaźniki obsługi błędów do naszej tablicy. Dodaj obszar stanu nad tablicą i poniżej przycisków upuszczania.

  1. Wstaw następujący znacznik po elemecie 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>
    

    Ten znacznik pozwala nam wyświetlać wskaźniki dla:

    • Ogłoszenie zwycięzcy gry
    • Przycisk, który pozwala nam ponownie uruchomić grę
    • Komunikaty o błędach
    • Kolej bieżącego gracza

    Teraz wypełnijmy logikę, która ustawia te wartości.

  2. Dodaj następujący kod po tablicy elementów:

    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;" : "";
    
    • Właściwość CurrentTurn jest obliczana automatycznie na podstawie stanu winnerMessage i PlayerTurn właściwości GameState.
    • Element ResetStyle jest obliczany na podstawie zawartości elementu WinnerMessage. Jeśli istnieje winnerMessageelement , spowoduje to wyświetlenie przycisku resetowania na ekranie.
  3. Obsłużmy komunikat o błędzie podczas odtwarzania elementu. Dodaj wiersz, aby wyczyścić komunikat o błędzie, a następnie opakuj kod w PlayPiece metodzie za pomocą try...catch bloku, aby ustawić errorMessage , czy wystąpił wyjątek:

    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;
    }
    

    Nasz wskaźnik obsługi błędów jest prosty i używa platformy CSS Bootstrap do wyświetlania błędu w trybie zagrożenia.

    Zrzut ekranu przedstawiający twoją grę do tej pory z deską i kawałkami.

  4. Następnie dodajmy metodę ResetGame wyzwalaną przez nasz przycisk w celu ponownego uruchomienia gry. Obecnie jedynym sposobem ponownego uruchomienia gry jest odświeżenie strony. Ten kod pozwala nam pozostać na tej samej stronie.

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

    Teraz nasza ResetGame metoda ma następującą logikę:

    • Zresetuj stan tablicy.
    • Ukryj nasze wskaźniki.
    • Zresetuj tablicę elementów do pustej tablicy 42 ciągów.

    Ta aktualizacja powinna pozwolić nam grać ponownie, a teraz widzimy wskaźnik tuż nad tablicą deklarując obrót gracza i ostatecznie zakończenie gry.

    Zrzut ekranu przedstawiający grę.

    Nadal mamy sytuacje, w których nie możemy wybrać przycisku resetuj. Dodajmy logikę w metodzie PlayPiece , która wykrywa koniec gry.

  5. Wykryjmy, czy w grze jest zwycięzca, dodając wyrażenie przełącznika po naszym try...catch bloku w pliku PlayPiece.

    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!",
        _ => ""
    };
    

    Metoda CheckForWin zwraca wyliczenie, które zgłasza, który gracz, jeśli którykolwiek wygrał grę lub jeśli gra jest krawat. To wyrażenie przełącznika odpowiednio ustawi pole, winnerMessage jeśli wystąpi gra w stanie.

    Teraz, gdy gramy i osiągniemy scenariusz kończący grę, wyświetlane są następujące wskaźniki:

    Zrzut ekranu przedstawiający resetowanie gry.

Podsumowanie

Dowiedzieliśmy się wiele o Blazor i zbudowaliśmy schludną małą grę. Poniżej przedstawiono niektóre umiejętności, których nauczyliśmy się:

  • Utworzono składnik
  • Dodano ten składnik do naszej strony głównej
  • Użyto iniekcji zależności do zarządzania stanem gry
  • Uczynił grę interaktywną z procedurami obsługi zdarzeń, aby umieścić kawałki i zresetować grę
  • Napisał procedurę obsługi błędów, aby zgłosić stan gry
  • Dodano parametry do naszego składnika

Projekt, który skompilowaliśmy, jest prostą grą i jest o wiele więcej, co można z nim zrobić. Szukasz pewnych wyzwań, aby go poprawić?

Wyzwania

Weź pod uwagę następujące wyzwania:

  • Aby zmniejszyć aplikację, usuń domyślny układ i dodatkowe strony.
  • Popraw parametry Board składnika, aby można było przekazać dowolną prawidłową wartość koloru CSS.
  • Popraw wygląd wskaźników za pomocą niektórych układów CSS i HTML.
  • Wprowadzenie efektów dźwiękowych.
  • Dodaj wskaźnik wizualizacji i uniemożliwia korzystanie z przycisku upuszczania, gdy kolumna jest pełna.
  • Dodaj możliwości sieciowe, aby można było odtworzyć znajomego w przeglądarce.
  • Wstaw grę do programu .NET MAUI z aplikacją Blazor i odtwórz ją na telefonie lub tablecie.

Szczęśliwe kodowanie i zabawa!