Übung : Spiellogik
In dieser Übung fügen wir unserer App Spiellogik hinzu, um sicherzustellen, dass wir am Ende ein voll funktionsfähiges Spiel haben.
Damit dieses Tutorial thematisch zu Blazor passt, wird eine Klasse namens GameState
bereitgestellt, die die Logik für die Verwaltung des Spiels enthält.
Hinzufügen des Spielstatus
Fügen Sie dem Projekt die GameState
-Klasse hinzu, und stellen Sie anschließend Komponenten über Dependency Injection als Singletondienst zur Verfügung.
Kopieren Sie die Datei GameState.cs in das Stammverzeichnis des Projekts.
Öffnen Sie die Datei Program.cs im Stammverzeichnis des Projekts und fügen Sie diese Anweisung hinzu, um
GameState
in Ihrer App als Singletondienst zu konfigurieren:builder.Services.AddSingleton<GameState>();
Jetzt können Sie eine Instanz der
GameState
-Klasse in dieBoard
-Komponente einfügen.Fügen Sie die folgende
@inject
-Direktive oben in der Datei Board.razor hinzu. die Direktive fügt den aktuellen Zustand des Spiels in die Komponente ein:@inject GameState State
Jetzt können Sie damit beginnen, die
Board
-Komponente mit dem Status des Spiels zu verknüpfen.
Zurücksetzen des Status
Beginnen Sie damit, den Status des Spiels zurückzusetzen, wenn die Board
-Komponente das erste Mal auf der Anzeige gerendert wird. Sie fügen Code hinzu, um den Status des Spiels zurückzusetzen, wenn die Komponente initialisiert wird.
Fügen Sie unten in der Datei Board.razor im
@code
-Block eineOnInitialized
-Methode mit einem Aufruf vonResetBoard
folgendermaßen hinzu:@code { protected override void OnInitialized() { State.ResetBoard(); } }
Wenn das Spielbrett zum ersten Mal einem*einer Benutzer*in gezeigt wird, wird der Status auf den Spielanfang zurückgesetzt.
Erstellen von Spielsteinen
Als Nächstes ordnen wir die möglichen 42 Spielsteine zu, die gespielt werden könnten. Wir können die Spielsteine als ein Array darstellen, auf das von 42 HTML-Elementen auf dem Spielbrett verwiesen wird. Wir können diese Steine verschieben und positionieren, indem wir mehrere CSS-Klassen mit Spalten- und Zeilenpositionen zuweisen.
Um unsere Spielstücke zu halten, definieren wir ein Zeichenfolgenarrayfeld im Codeblock:
private string[] pieces = new string[42];
Fügen Sie dem HTML-Abschnitt Code hinzu, der 42
span
-Tags (einen für jeden Spielstein) in derselben Komponente erzeugt:@for (var i = 0; i < 42; i++) { <span class="@pieces[i]"></span> }
Der vollständige Code sollte wie folgt aussehen:
<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(); } }
Dadurch wird der CSS-Klasse jedes Spielabschnitts eine leere Zeichenfolge zugewiesen. Eine leere Zeichenfolge für eine CSS-Klasse verhindert, dass die Spielsteine auf dem Bildschirm erscheinen, da kein Stil auf sie angewendet wird.
Verarbeiten der Positionierung von Spielsteinen
Fügen Sie eine Methode hinzu, die das Einwerfen eines Spielsteins in eine Spalte durch ein*e Spieler*in verarbeitet. Die GameState
-Klasse kann die richtige Zeile für den Spielstein zuweisen und meldet die Zeile, in der er abgelegt wird. Mit diesen Informationen können wir CSS-Klassen zuweisen, die die Farbe des Spielers bzw. der Spielerin, die endgültige Position des Spielsteins und eine CSS-Positionierungsanimation darstellen.
Diese Methode wird als PlayPiece
bezeichnet und akzeptiert einen Eingabeparameter, der die Spalte angibt, die von dem oder der Spieler*in ausgewählt wird.
Fügen Sie diesen Code unterhalb des
pieces
-Arrays ein, das wir im vorherigen Schritt definiert haben.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}"; }
Das ist die Funktionsweise des PlayPiece
-Codes:
- Wir weisen den Spielstatus an, einen Spielstein in der angegebenen Spalte namens
col
zu spielen und die Zeile, in der der Spielstein gelandet ist, zu erfassen. - Anschließend können Sie die drei CSS-Klassen definieren, die dem Spielstein zugewiesen werden sollen, um Folgendes zu ermitteln: den oder die aktuell aktive*n Spieler*in, die Spalte, in die der Spielstein eingeworfen wurde, und die Zeile, in der er landet.
- Die letzte Zeile der Methode weist diese Klassen dem Spielstein im
pieces
-Array zu.
Wenn Sie in der bereitgestellten Datei Board.razor.css nachsehen, finden Sie die CSS-Klassen für Spalte, Zeile und Spielerzug.
Das Ergebnis ist, dass der Spielstein in der Spalte platziert und so animiert wird, dass er in die unterste Zeile fällt, wenn diese Methode aufgerufen wird.
Auswählen einer Spalte
Als Nächstes müssen wir einige Steuerelemente hinzufügen, mit denen die Spieler*innen eine Spalte auswählen können, und unsere neue PlayPiece
-Methode aufrufen. Verwenden Sie das Zeichen 🔽, um anzugeben, dass Sie einen Spielstein in diese Spalte einwerfen können.
Fügen Sie oberhalb des
<div>
-Starttags eine Zeile mit klickbaren Schaltflächen hinzu:<nav> @for (byte i = 0; i < 7; i++) { var col = i; <span title="Click to play a piece" @onclick="() => PlayPiece(col)">🔽</span> } </nav>
Das
@onclick
-Attribut gibt einen Ereignishandler für das Klickereignis an. Um jedoch Ereignisse auf der Benutzeroberfläche zu verarbeiten, muss eine Blazor-Komponente mithilfe des interaktiven Rendermodus gerendert werden. Standardmäßig werden Blazor-Komponenten statisch vom Server gerendert. Mithilfe des@rendermode
-Attributs können wir den interaktiven Rendermodus auf eine Komponente anwenden.Aktualisieren Sie die
Board
-Komponente auf derHome
-Seite so, dass sie denInteractiveServer
-Rendermodus verwendet.<Board @rendermode="InteractiveServer" />
Der
InteractiveServer
-Rendermodus verarbeitet Ereignisse auf der Benutzeroberfläche für Ihre Komponenten vom Server über eine WebSocket-Verbindung mit dem Browser.Führen Sie die App mit diesen Änderungen aus. Sie sollte jetzt folgendermaßen aussehen:
Wenn Sie oben eine der Schaltflächen zum Einwerfen auswählen, kann das folgende Verhalten beobachtet werden:
Toller Fortschritt! Wir können dem Spielbrett jetzt Spielsteine hinzufügen. Das GameState
-Objekt ist intelligent genug, um zwischen den beiden Spieler*innen zu wechseln. Wählen Sie weitere Schaltflächen zum Ablegen aus, und überprüfen Sie die Ergebnisse.
Umgang mit Siegen und Verlusten
Wenn Sie das Spiel in seiner aktuellen Konfiguration spielen, stellen Sie fest, dass es Fehler auslöst, wenn Sie versuchen, zu viele Teile in derselben Spalte zu platzieren und wenn ein Spieler das Spiel gewinnt.
Lassen Sie uns den aktuellen Zustand unseres Spiels deutlich machen, indem wir unserer Tafel einige Fehlerbehandlungs- und Indikatoren hinzufügen. Sie fügen einen Statusbereich über dem Spielbrett und unterhalb der Schaltflächen zum Einwerfen hinzu.
Fügen Sie das folgende Markup nach dem
nav
-Element ein:<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>
Dieses Markup ermöglicht das Anzeigen von Indikatoren für Folgendes:
- Bekanntgabe des Gewinners bzw. der Gewinnerin eines Spiels
- Eine Schaltfläche, mit der das Spiel neu gestartet werden kann
- Fehlermeldungen
- Aktueller Zug des Spielers bzw. der Spielerin
Lassen Sie uns nun eine Logik einfügen, um diese Werte festzulegen.
Fügen Sie nach dem Array für die Spielsteine den folgenden Code hinzu:
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;" : "";
- Die
CurrentTurn
-Eigenschaft wird basierend auf dem Status derGameState
-EigenschaftenwinnerMessage
undPlayerTurn
berechnet. - Der
ResetStyle
wird basierend auf dem Inhalt derWinnerMessage
berechnet. Wenn einewinnerMessage
vorhanden ist, wird die Schaltfläche zum Zurücksetzen auf dem Bildschirm angezeigt.
- Die
Behandeln wir die Fehlermeldung, wenn ein Spielstein gespielt wird. Fügen Sie eine Zeile hinzu, um die Fehlermeldung zu löschen, und brechen Sie anschließend den Code in der
PlayPiece
-Methode mit einemtry...catch
-Block um, um dieerrorMessage
-Komponente beim Auftreten einer Ausnahme festzulegen: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; }
Der Fehlerhandlerindikator ist einfach und verwendet das CSS-Bootstrapframework, um einen Fehler im Gefahrenmodus anzuzeigen.
Als Nächstes fügen wir die
ResetGame
-Methode hinzu, die unsere Schaltfläche aktiviert, um ein Spiel neu zu starten. Derzeit besteht die einzige Möglichkeit zum Beginnen eines neuen Spiels darin, die Seite zu aktualisieren. Dieser Code ermöglicht es den Spieler*innen, auf derselben Seite zu bleiben.void ResetGame() { State.ResetBoard(); winnerMessage = string.Empty; errorMessage = string.Empty; pieces = new string[42]; }
Unsere
ResetGame
-Methode hat nun die folgende Logik:- Der Status des Spielbretts wird zurückgesetzt.
- Die Indikatoren werden ausgeblendet.
- Das Array für die Spielsteine wird auf ein leeres Array mit 42 Zeichenfolgen zurückgesetzt.
Mit diesem Update sollte es möglich sein, das Spiel wieder zu spielen. Jetzt ist ein Indikator direkt über dem Spielbrett zu sehen, der anzeigt, dass der*die Spieler*in an der Reihe ist und das Spiel eventuell beendet wird.
Wir können die Schaltfläche zum Zurücksetzen immer noch nicht auswählen. Lassen Sie uns nun eine Logik in die
PlayPiece
-Methode einfügen, um das Ende des Spiels zu ermitteln.Um eine*n Gewinner*in zu ermitteln, fügen wir einen switch-Ausdruck nach unserem
try...catch
-Block inPlayPiece
ein.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!", _ => "" };
Die
CheckForWin
-Methode gibt eine Enumeration zurück, die angibt, ob ein*e Spieler*in das Spiel gewonnen hat oder ob es unentschieden steht. Dieser switch-Ausdruck legt daswinnerMessage
-Feld entsprechend fest, wenn ein*e Spieler*in das Spiel verloren hat.Wenn wir nun spielen und ein Szenario eintritt, in dem das Spiel beendet wird, werden diese Indikatoren angezeigt:
Zusammenfassung
Wir haben eine Menge über Blazor gelernt und ein nettes kleines Spiel entwickelt. Hier finden Sie einige der Fähigkeiten, die wir erworben haben:
- Erstellen einer Komponente
- Hinzufügen einer Komponente zur Homepage
- Verwenden der Abhängigkeitsinjektion zum Verwalten des Status eines Spiels
- Interaktive Gestaltung des Spiels mit Ereignishandlern zum Platzieren von Spielsteinen und zum Zurücksetzen des Spiels
- Schreiben eines Fehlerhandlers zur Meldung des Spielstatus
- Hinzufügen von Parametern zur Komponente
Das von uns entwickelte Projekt ist ein einfaches Spiel, mit dem Sie noch viel mehr anfangen können. Sind Sie auf der Suche nach Herausforderungen, um es zu verbessern?
Herausforderungen
Hier finden Sie mögliche Herausforderungen:
- Um die App zu verkleineren, entfernen Sie das Standardlayout und zusätzliche Seiten.
- Verbessern der Parameter für die
Board
-Komponente, um einen beliebigen gültigen CSS-Farbwert übergeben zu können - Verbessern der Darstellung der Indikatoren mit CSS- und HTML-Layout
- Hinzufügen von Soundeffekten
- Hinzufügen eines visuellen Indikators und Verhindern, dass die Schaltfläche zum Einwerfen angezeigt wird, wenn die Spalte voll ist
- Hinzufügen von Netzwerkfunktionen, um im Browser gegen Freunde spielen zu können
- Fügen Sie das Spiel in eine .NET MAUI Blazor-Anwendung ein, und spielen Sie es auf Ihrem Smartphone oder Tablet.
Viel Spaß beim Programmieren!