Oefening: gamelogica

Voltooid

In deze oefening voegen we gamelogica toe aan onze app om ervoor te zorgen dat we uiteindelijk een volledig functionerend spel hebben.

Om deze zelfstudie on-topic te houden met lesgeven over Blazor, bieden we een klasse GameState die de logica bevat voor het beheren van het spel.

Gamestatus toevoegen

We gaan de GameState klasse toevoegen aan uw project en deze vervolgens beschikbaar maken voor onderdelen als een singleton-service via afhankelijkheidsinjectie.

  1. Kopieer het GameState.cs bestand naar de hoofdmap van uw project.

  2. Open het Program.cs-bestand in de hoofdmap van het project en voeg deze instructie toe waarmee GameState als een singleton-service in uw app wordt geconfigureerd:

    builder.Services.AddSingleton<GameState>();
    

    We kunnen nu een exemplaar van de GameState klasse in ons Board onderdeel injecteren.

  3. Voeg de volgende @inject instructie toe bovenaan het Board.razor-bestand . de richtlijn injecteert de huidige status van het spel in het onderdeel:

    @inject GameState State
    

    We kunnen nu beginnen met het verbinden van ons Board onderdeel met de status van het spel.

Status opnieuw instellen

Laten we beginnen met het opnieuw instellen van de status van het spel wanneer het onderdeel voor het eerst op het Board scherm wordt geschilderd. Voeg code toe om de status van de game opnieuw in te stellen wanneer het onderdeel wordt geïnitialiseerd.

  1. Voeg een OnInitialized methode toe met een aanroep naar ResetBoard, in het @code blok onderaan het Board.razor-bestand , zoals:

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

    Wanneer het bord voor het eerst aan een gebruiker wordt getoond, wordt de status opnieuw ingesteld op het begin van een game.

Game-onderdelen maken

Vervolgens gaan we de mogelijke 42 spelstukken toewijzen die kunnen worden gespeeld. We kunnen de spelstukken vertegenwoordigen als een matrix waarnaar wordt verwezen door 42 HTML-elementen op het bord. We kunnen deze onderdelen verplaatsen en plaatsen door een set CSS-klassen met kolom- en rijposities toe te wijzen.

  1. Om onze spelonderdelen te bewaren, definiëren we een tekenreeksmatrixveld in het codeblok:

    private string[] pieces = new string[42];
    
  2. Voeg code toe aan de HTML-sectie waarmee 42 span tags, één voor elk spelstuk, in hetzelfde onderdeel worden gemaakt:

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

    Uw volledige code moet er als volgt uitzien:

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

    Hiermee wordt een lege tekenreeks toegewezen aan de CSS-klasse van elk stuk van het spel. Een lege tekenreeks voor een CSS-klasse voorkomt dat de spelstukken op het scherm worden weergegeven, omdat er geen stijl op deze onderdelen wordt toegepast.

Plaatsing van spelstuk verwerken

Laten we een methode toevoegen die moet worden verwerkt wanneer een speler een stuk in een kolom plaatst. De GameState klasse weet hoe de juiste rij voor het spelstuk moet worden toegewezen en rapporteert de rij waarin deze terechtkomt. We kunnen deze informatie gebruiken om CSS-klassen toe te wijzen die de kleur van de speler vertegenwoordigen, de uiteindelijke locatie van het stuk en een CSS-neerloopanimatie.

Deze methode PlayPiecewordt aangeroepen en er wordt een invoerparameter geaccepteerd die de kolom aangeeft die de speler kiest.

  1. Voeg deze code toe onder de pieces matrix die we in de vorige stap hebben gedefinieerd.

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

Dit is wat de PlayPiece code doet:

  1. We vertellen de gamestatus om een stuk in de ingediende kolom col te spelen en de rij vast te leggen waarin het stuk terecht is gekomen.
  2. Vervolgens kunnen we de drie CSS-klassen definiëren die aan het spelstuk moeten worden toegewezen om te bepalen welke speler momenteel werkt, de kolom waarin het stuk is geplaatst en de landingsrij.
  3. De laatste regel van de methode wijst deze klassen toe aan dat spelstuk in de pieces matrix.

Als u in de Board.razor.css kijkt, vindt u de CSS-klassen die overeenkomen met de kolom, rij en de beurt van de speler.

Het resulterende effect is dat het spelstuk wordt geplaatst in de kolom en geanimeerde om in de onderste rij te vallen wanneer deze methode wordt aangeroepen.

Een kolom kiezen

We moeten vervolgens enkele besturingselementen plaatsen waarmee spelers een kolom kunnen kiezen en onze nieuwe PlayPiece methode kunnen aanroepen. We gebruiken het teken '🔽' om aan te geven dat u een stuk in deze kolom kunt neerzetten.

  1. Voeg boven de starttag <div> een rij met klikbare knoppen toe:

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

    Het @onclick kenmerk geeft een gebeurtenis-handler op voor de klik-gebeurtenis. Maar voor het afhandelen van UI-gebeurtenissen moet een Blazor-onderdeel worden weergegeven met behulp van een interactieve rendermodus. Blazor-onderdelen worden standaard statisch van de server weergegeven. We kunnen een interactieve rendermodus toepassen op een onderdeel met behulp van het @rendermode kenmerk.

  2. Werk het Board onderdeel op de Home pagina bij zodat het de InteractiveServer rendermodus gebruikt.

    <Board @rendermode="InteractiveServer" />
    

    De InteractiveServer rendermodus verwerkt ui-gebeurtenissen voor uw onderdelen van de server via een WebSocket-verbinding met de browser.

  3. Voer de app uit met deze wijzigingen. Deze ziet er nu als volgt uit:

    Schermopname van Connect Four board.

    Nog beter, wanneer we bovenaan een van de drop-knoppen selecteren, kan het volgende gedrag worden waargenomen:

    Schermopname van Verbinding maken met vier animaties.

Goed bezig! We kunnen nu stukken aan het bord toevoegen. Het GameState object is slim genoeg om heen en weer te draaien tussen de twee spelers. Ga verder en selecteer meer drop-knoppen en bekijk de resultaten.

Winnende en foutafhandeling

Als u het spel in de huidige configuratie speelt, merkt u dat het fouten veroorzaakt wanneer u te veel stukken in dezelfde kolom probeert te plaatsen en wanneer één speler het spel wint.

Laten we de huidige status van ons spel duidelijk maken door een aantal foutafhandeling en indicatoren toe te voegen aan ons bord. Voeg een statusgebied toe boven het bord en onder de knoppen voor neerzetten.

  1. Voeg de volgende markeringen toe na het nav element:

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

    Met deze markering kunnen we indicatoren weergeven voor:

    • Aankondiging van een wedstrijdwinnaar
    • Een knop waarmee we de game opnieuw kunnen starten
    • Foutberichten
    • De beurt van de huidige speler

    Laten we nu een aantal logica invullen waarmee deze waarden worden ingesteld.

  2. Voeg de volgende code toe na de stukmatrix:

    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;" : "";
    
    • De CurrentTurn eigenschap wordt automatisch berekend op basis van de status van de winnerMessage en de PlayerTurn eigenschap van de GameState.
    • De ResetStyle wordt berekend op basis van de inhoud van de WinnerMessage. Als er een winnerMessageis, wordt de knop Opnieuw instellen weergegeven op het scherm.
  3. Laten we het foutbericht afhandelen wanneer een stuk wordt afgespeeld. Voeg een regel toe om het foutbericht te wissen en verpakt de code vervolgens in de PlayPiece methode met een try...catch blok om in errorMessage te stellen of er een uitzondering is opgetreden:

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

    Onze indicator voor fouthandler is eenvoudig en maakt gebruik van het Bootstrap CSS-framework om een fout in de gevaarmodus weer te geven.

    Schermopname van je spel tot nu toe, met een bord en stukken.

  4. Vervolgens gaan we de ResetGame methode toevoegen die door de knop wordt geactiveerd om een game opnieuw te starten. Momenteel is de enige manier om een game opnieuw te starten de pagina te vernieuwen. Met deze code kunnen we op dezelfde pagina blijven.

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

    ResetGame Onze methode heeft nu de volgende logica:

    • Stel de status van het bord opnieuw in.
    • Verberg onze indicatoren.
    • Stel de stukkenmatrix opnieuw in op een lege matrix van 42 tekenreeksen.

    Met deze update kunnen we het spel opnieuw spelen en nu zien we een indicator net boven het bord dat de beurt van de speler aangeeft en uiteindelijk de voltooiing van het spel.

    Schermopname van game over.

    We hebben nog steeds een situatie waarin we de knop Opnieuw instellen niet kunnen selecteren. Laten we wat logica toevoegen aan de PlayPiece methode waarmee het einde van het spel wordt gedetecteerd.

  5. Laten we eens kijken of er een winnaar aan het spel is door na ons try...catch blok PlayPieceeen switchexpressie toe te voegen.

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

    De CheckForWin methode retourneert een opsomming die rapporteert welke speler, indien aanwezig het spel heeft gewonnen of als het spel een gelijkspel is. Met deze switchexpressie wordt het veld op de winnerMessage juiste manier ingesteld als er een game over de status plaatsvindt.

    Wanneer we nu een scenario met speleindpunten spelen en bereiken, worden deze indicatoren weergegeven:

    Schermopname van het opnieuw instellen van de game.

Samenvatting

We hebben veel geleerd over Blazor en een netjes klein spel gebouwd. Hier volgen enkele van de vaardigheden die we hebben geleerd:

  • Een onderdeel gemaakt
  • Dat onderdeel toegevoegd aan onze startpagina
  • Gebruikte afhankelijkheidsinjectie om de status van een game te beheren
  • Het spel interactief gemaakt met gebeurtenis-handlers om stukken te plaatsen en het spel opnieuw in te stellen
  • Schreef een fouthandler om de status van het spel te rapporteren
  • Parameters toegevoegd aan ons onderdeel

Het project dat we hebben gebouwd, is een eenvoudig spel en er is nog veel meer dat je ermee kunt doen. Zoekt u een aantal uitdagingen om het te verbeteren?

Uitdagingen

Houd rekening met de volgende uitdagingen:

  • Als u de app kleiner wilt maken, verwijdert u de standaardindeling en extra pagina's.
  • Verbeter de parameters voor het Board onderdeel, zodat u elke geldige CSS-kleurwaarde kunt doorgeven.
  • Verbeter het uiterlijk van indicatoren met een CSS- en HTML-indeling.
  • Introduceer geluidseffecten.
  • Voeg een visuele indicator toe en voorkom dat er een drop button wordt gebruikt wanneer de kolom vol is.
  • Voeg netwerkmogelijkheden toe zodat u een vriend in hun browser kunt afspelen.
  • Plaats het spel in een .NET MAUI met Blazor-toepassing en speel het op uw telefoon of tablet.

Gelukkig coderen en plezier hebben!