Windows Phone 7-Spieleentwicklung Erstellen von Tic-Tac-Toe mit XNA

XNA ist ein hochentwickeltes Framework zur Erstellung von Spielen für Microsoft-Geräte einschließlich Windows-PCs, Xbox 360 und das brandneue Windows Phone 7-Betriebssystem. In einem früheren Tutorial haben wir die Grundlagen des XNA-Frameworks behandelt, einschließlich der Verarbeitung von Eingaben und der Anzeige von Sprites. In diesem Artikel erfahren Sie, wie Sie diese Fähigkeiten mit einer eigenen Spielidee kombinieren, um etwas Spaß zu schaffen und leicht zu erlernen.

Projektübersicht

In diesem Tutorial erstellen Sie ein einfaches Tic-Tac-Toe-Spiel, das Sie mit Freunden spielen können. Tic-Tac-Toe ist ein einfaches Spiel, bei dem zwei Spieler abwechselnd Züge auf ein Drei-zu-Drei-Raster setzen. Der erste Spieler verwendet ein O und der zweite Spieler ein X. Um das Spiel zu gewinnen, muss ein Spieler drei seiner Charaktere in eine Reihe, eine Spalte oder eine Diagonale einordnen.

Dieses Spiel ist zwar einfach, erfordert jedoch ein paar unterschiedliche Fähigkeiten. Zuerst verwenden Sie XNA und seine Grafikfunktionen, um Ihr Spiel zu erstellen. Daher müssen Sie mit der Anzeige von Sprites vertraut sein. Zweitens müssen Sie wissen, wie Sie mit dem Touchpanel des Telefons umgehen. Glücklicherweise bietet XNA eine API auf hoher Ebene, um auf diese Berührungen zuzugreifen. Schließlich müssen Sie ein wenig Programmier- und Logik-Know-how anwenden, um den Gewinner zu bestimmen. Für dieses Tutorial wird Ihnen viel davon gegeben. Wenn Sie in Zukunft Spiele entwickeln, müssen Sie Ihre eigenen Ideen und Ihre eigene Logik entwickeln.

Projekt erstellen

Stellen Sie zunächst sicher, dass Sie die neuesten Windows Phone 7-Entwicklungstools installiert haben. Wenn Sie Ihre Tools seit dem letzten WP7-Lernprogramm für MobileTuts nicht aktualisiert haben, sollten Sie das Microsoft Download Center besuchen und die RTW-Version erhalten. Diese neueste Version wird mit dem endgültigen Emulator geliefert, der zeigt, wie Ihre Apps auf der Hardware des Releasetags funktionieren.

Wenn Sie sichergestellt haben, dass Ihre Tools auf dem neuesten Stand sind, öffnen Sie Visual Studio 2010 und klicken Sie in der linken Seitenleiste auf den Link „Neues Projekt…“. Wählen Sie im daraufhin angezeigten Dialogfeld "XNA Game Studio 4" in der linken Spalte aus und stellen Sie sicher, dass rechts die Vorlage "Windows Phone Game (4.0)" ausgewählt ist. Geben Sie Ihrem Projekt einen geeigneten Namen wie "TicTacToe" und bestätigen Sie, dass das Kontrollkästchen "Verzeichnis für Lösung erstellen" aktiviert ist. Wenn Sie dies alles richtig gemacht haben, sollte Ihr Dialogfeld mit dem folgenden Bild übereinstimmen:

Klicken Sie auf "OK", um Ihr neues Projekt zu erstellen. Visual Studio 2010 generiert die erforderlichen Dateien in Ihrem angegebenen Verzeichnis und öffnet sie Game1.cs zur Bearbeitung.

Medieninhalt importieren

Da Sie Sprites für alle Spielgrafiken in diesem Projekt verwenden, müssen Sie die erforderlichen Elemente in Ihr Projekt importieren. Im Download dieses Tutorials finden Sie eine Medien Verzeichnis mit einer Vielzahl von Bildern. Suchen Sie im Solution Explorer auf der rechten Seite des Bildschirms das Content-Projekt (TicTacToeContent) und klicken Sie mit der rechten Maustaste darauf. Wählen Sie im Kontextmenü „Hinzufügen> Vorhandenes Element…“. Wenn sich das Dialogfeld öffnet, navigieren Sie zu Medien Ordner, den Sie aus dem Tutorial-Download entpackt haben, und wählen Sie alle darin enthaltenen Bilder aus. Sie sollten in der Lage sein, anhand der Bildnamen genau zu erkennen, was jedes Element enthält.

Nachdem Sie Ihre Medien importiert haben, sollte Ihr Solution Explorer folgendermaßen aussehen:

Inhalte laden und zuweisen

Nachdem Ihr Medium in das an Ihre Lösung angehängte Content-Projekt importiert wurde, müssen Sie jedes Bild als separate Textur laden. Da Sie diese Texturen im gesamten Spiel verwenden, werden Sie sie in Feldern Ihrer Spielklasse speichern. Öffnen Sie die Datei Game1.cs und fügen Sie die folgenden Felder oben in Ihre Klassendeklaration ein:

 Texture2D gridTexture; Rechteck gridRectangle; Texture2D resetButton; Rechteck resetButtonPosition; Texture2D oPiece; Texture2D xPiece; Texture2D oWinner; Texture2D xWinner; Texture2D noWinner; Texture2D oTurn; Texture2D xTurn; 

Sie sehen, dass jedes der importierten Bilder ein Feld hat, um es zu sichern. Zusätzlich haben das Hauptspielgitter und die Textur der Rücksetztasten Felder vom Typ Rechteck die zum Positionieren dieser Elemente verwendet wird. Diese werden als Felder gespeichert, da sie sich im Laufe des Spiels nicht ändern.

Nachdem Sie nun die entsprechenden Felder erstellt haben, ist es Zeit, das zu instanziieren Textur2D und Rechteck Objekte, die den Feldern zugeordnet sind. Scrollen Sie nach unten zu Ihrem Game1.cs Datei, bis Sie die LoadContent Methode. Fügen Sie innerhalb dieser Methode den folgenden Code nach der Zeile ein, die liest spriteBatch = new SpriteBatch (GraphicsDevice);:

 gridTexture = Content.Load("TicTacToe_Grid"); gridRectangle = new Rectangle (0, 0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height); oPiece = Content.Load("TicTacToe_O"); xPiece = Content.Load("TicTacToe_X"); resetButton = Content.Load("TicTacToe_Reset"); resetButtonPosition = new Rectangle (spriteBatch.GraphicsDevice.Viewport.Width / 2 - (resetButton.Width / 2), spriteBatch.GraphicsDevice.Viewport.Height - 95, resetButton.Width); oWinner = Content.Load("TicTacToe_O_Winner"); xWinner = Content.Load("TicTacToe_X_Winner"); noWinner = Content.Load("TicTacToe_Draw"); oTurn = Content.Load("TicTacToe_O_Turn"); xTurn = Content.Load("TicTacToe_X_Turn"); 

Definieren des Spielablaufs

Nachdem Sie die Sprites geladen haben, die vom Spiel verwendet werden, müssen Sie den Workflow festlegen, mit dem das Spiel ausgeführt wird. Für die Zwecke von Tic-Tac-Toe ist dies ziemlich einfach und sieht in etwa wie folgt aus:

  1. Zeichnen Sie das Spielfeld
  2. Zeichne die aktuell gespielten Stücke
  3. Zeichne den aktuellen Spielstatus (wer an der Reihe ist oder ob es einen Gewinner gibt)
  4. Wenn das Spiel gewonnen wurde oder nicht mehr gewinnbar ist, ziehen Sie den Reset-Button
  5. Wenn niemand gewonnen hat und das Spiel immer noch gewinnbar ist, berührt der Griff das Raster und prüft, ob ein Gewinner ermittelt wurde
  6. Wenn das Spiel gewonnen wurde oder nicht mehr gewinnbar ist, berührt der Griff die Rücksetztaste
    • Wenn Sie die Reset-Taste berühren, setzen Sie das Spiel zurück

Wie Sie wahrscheinlich feststellen können, lassen sich diese Schritte in eine von zwei grundlegenden Kategorien einteilen: Zeichnen oder Aktualisieren. Durchsuchen der Game1.cs Datei Sie werden sehen, dass Sie zwei Methoden haben, Zeichnen und Aktualisieren Das sind die perfekten Container für den Code, der zur Beschreibung des Workflows erforderlich ist.

Spielzustand speichern

Wenn Sie sich den Arbeitsablauf anschauen, können Sie feststellen, dass es vier verschiedene Elemente gibt, über die Sie nachverfolgen können, um den Spielstatus zu verwalten. Zunächst müssen Sie den Status jedes Rasterfeldes auf dem Spielfeld verfolgen. Dazu verwenden Sie eine benutzerdefinierte Aufzählung und ein mehrdimensionales Array. Als Nächstes müssen Sie nachverfolgen, ob und von wem das Spiel gewonnen wurde. Drittens müssen Sie verfolgen, wer an der Reihe ist. Schließlich müssen Sie nachverfolgen, ob das Spiel gewinnbar ist. Dieses Element wird als initialisiert wahr und wird neu berechnet, nachdem jeder Spieler an der Reihe ist. Wenn alle Rasterfelder ausgefüllt sind, ändert sich dies zu falsch.

Sie beginnen mit der Definition der benutzerdefinierten Aufzählung, die einen Spieler für Ihr Spiel beschreibt. Fügen Sie oben in Ihrer Klassendatei über Ihrer Klassendeklaration den folgenden Code ein:

 public enum TicTacToePlayer Keine, PlayerO, PlayerX 

Diese Aufzählung umfasst drei Spieler (None, PlayerO und PlayerX) und wird verwendet, um sowohl den Gitterstatus als auch den Spielgewinner zu verfolgen. Fügen Sie nun die Instanzvariablen hinzu, mit denen Sie den Status des Spiels nachverfolgen können. Diese Elemente sollten oben in Ihrer Klassendeklaration eingefügt werden:

 Bool gewinnbar; TicTacToePlayer-Gewinner; TicTacToePlayer aktuell; TicTacToePlayer [,] Raster; 

Sie speichern hier vier Dinge. Zuerst legen Sie fest, ob das Spiel noch gewinnbar ist. Zweitens erklären Sie den Gewinner. Wenn der Gewinner keine ist, wird das Spiel fortgesetzt. Danach speichern Sie den aktuellen Player. Zum Schluss speichern Sie den Netzzustand. Zu diesem Zeitpunkt müssen Sie noch jede dieser Variablen initialisieren. Wenn Sie vorausdenken, wissen Sie, dass Sie sie jedes Mal neu initialisieren müssen, wenn jemand auf die Schaltfläche Zurücksetzen klickt. Daher können wir eine neue Methode erstellen, die diese Initialisierung handhabt. Fügen Sie Ihrer Klasse die neue Methode unter dem hinzu Initialisieren Methode wie folgt:

 privat void Reset () gewinnbar = wahr; Gewinner = TicTacToePlayer.None; grid = neuer TicTacToePlayer [3, 3]; für (int i = 0; i < 3; i++)  for (int j = 0; j < 3; j++)  grid[i, j] = TicTacToePlayer.None;    

Rufen Sie nun diese neue Methode aus der Initialisieren Methode, indem Sie es wie folgt ändern:

 protected override void Initialize () Reset (); base.Initialize ();  

An diesem Punkt speichern Sie alle Daten, die Sie benötigen, daher sollte es einfach sein, die Oberfläche zu zeichnen.

Zeichnen der Spieloberfläche

Bevor Sie mit dem Zeichnen beginnen, müssen Sie Ihre bevorzugte Breite und Höhe für Ihr Gerät festlegen. Sie werden dies innerhalb der tun Game1 Konstrukteur. Ändern Sie es wie folgt zu lesen:

 public Game1 () graphics = new GraphicsDeviceManager (this); Content.RootDirectory = "Inhalt"; graphics.PreferredBackBufferWidth = 480; graphics.PreferredBackBufferHeight = 800; // Die Bildrate beträgt standardmäßig 30 fps für Windows Phone. TargetElapsedTime = TimeSpan.FromTicks (333333);  

Sie haben Anweisungen hinzugefügt, dass die Breite des hinteren Puffers 480 Pixel und die Höhe 800 Pixel betragen soll. Dies vereinfacht das Zeichnen der restlichen Komponenten für das Spiel erheblich.

Der Einfachheit halber führen Sie jeden Zeichnungsschritt in einer separaten Methode aus. Diese Methoden werden dann von der Basis aus aufgerufen Zeichnen Methode, die bereits existiert. Beginnen wir damit, einige gute Namen für jeden der Workflow-Schritte des Spiels zu überdenken, Methoden dieses Namens zu erstellen und diesen Methoden Aufrufe von hinzuzugeben Zeichnen. Meines Erachtens beschreiben die folgenden Methodennamen sowohl ihre Arbeitsweise als auch die Arbeitsschritte des Arbeitsablaufs ausreichend:

  • DrawGrid - Deckt Arbeitsschritt 1 ab und zeichnet das Abspielraster
  • DrawPieces - Deckt den Workflow-Schritt zwei ab und zeichnet Stücke, wo ein Stück gespielt wurde
  • DrawStatus - Deckt den Arbeitsschritt 3 ab und zeichnet den aktuellen Spielstatus ("O's Turn", "X's Turn", "O Wins!", "X Wins!" Oder "Draw!").
  • DrawResetButton - Deckt Arbeitsschritt 4 ab und zeichnet die Rücksetztaste

Erstellen Sie diese Methoden jetzt, indem Sie den folgenden Code unterhalb Ihres einfügen Zeichnen Methode:

 private void DrawGrid ()  private void DrawPieces ()  private void DrawStatus ()  private void DrawResetButton ()  

Sie schreiben den Code für diese Methoden in Kürze, aber jetzt müssen Sie sie zu Ihrem hinzufügen Zeichnen Methode, indem Sie es wie folgt ändern:

 protected override void Draw (GameTime gameTime) GraphicsDevice.Clear (Color.Black); spriteBatch.Begin (); DrawGrid (); DrawPieces (); DrawStatus (); DrawResetButton (); spriteBatch.End (); base.Draw (gameTime);  

Sie werden bemerken, dass Sie die Aufrufe Ihrer Hilfemethoden mit Anrufen in umgeben haben spriteBatch Objekt ist Start und Ende Methoden. Sie tun dies, weil Ihre Hilfemethoden alle Sprites zeichnen und das Aufruf von Start und Ende Paar einmal in Zeichnen anstatt innerhalb jeder Hilfsmethode.

Lassen Sie uns nun daran arbeiten, dass das Spielfeld angezeigt wird. Das Wiedergabegitter ist ein 480 Pixel breites und 800 Pixel hohes Bild mit einem transparenten Hintergrund und einem weißen Raster von 150 Pixel breiten Feldern in der Mitte. Sie haben es früher importiert. Das Zeichnen auf dem Bildschirm des Telefons könnte nicht einfacher sein. Sie nehmen die geladene und gespeicherte Textur in die gridTexture Variable und zeichnen Sie es, indem Sie es mit dem zuvor instanziierten positionieren gridRectangle Variable, beide Elemente an die übergeben spriteBatch Objekt ist Zeichnen Methode. Ändere dein DrawGrid Methode wie folgt zu lesen:

 private void DrawGrid () spriteBatch.Draw (gridTexture, gridRectangle, Color.White);  

Speichern Sie die Datei, in der Sie arbeiten, und drücken Sie F5, um Ihr Projekt zu kompilieren und auszuführen. Der Windows Phone 7-Emulator sollte erscheinen und Ihr Tic-Tac-Toe-Raster auf schwarzem Hintergrund anzeigen, genau wie das folgende Bild:

Nun, da das Raster vorhanden ist, zeichnen wir die Spielsteine. An dieser Stelle können wir eine einfache Logik anwenden, um die Stücke anzuzeigen, aber nichts wird angezeigt, bis wir den Code hinzufügen, um Berührungen zu handhaben und das Spiel zu spielen. Ändern Sie Ihre DrawPieces Methode wie folgt:

 private void DrawPieces () für (int i = 0; i < 3; i++)  for (int j = 0; j < 3; j++)  if (grid[i, j] != TicTacToePlayer.None)  Texture2D texture = grid[i, j] == TicTacToePlayer.PlayerO ? oPiece : xPiece; Rectangle position = GetGridSpace(i, j, texture.Width, texture.Height); spriteBatch.Draw(texture, position, Color.White);     

Wenn Sie ein scharfes Auge haben (oder wahrscheinlicher, Visual Studio zeigt Ihnen rote Linien), werden Sie sehen, dass das GetGridSpace Methode fehlt. GetGridSpace ist eine bequeme Methode, die dazu beiträgt, dass ein wenig Code aus dem DrawPieces Methode und wird sich auch als nützlich erweisen, wenn Sie später versuchen, Berührungen zu bearbeiten. Fügen Sie es wie folgt am Ende Ihrer Klassendeklaration hinzu:

 privates Rechteck GetGridSpace (int-Spalte, int-Zeile, Int-Breite, Int-Höhe) int centerX = spriteBatch.GraphicsDevice.Viewport.Width / 2; int centerY = spriteBatch.GraphicsDevice.Viewport.Height / 2; int x = centerX + ((Spalte - 1) * 150) - (Breite / 2); int y = centerY + ((Reihe - 1) * 150) - (Höhe / 2); Neues Rechteck zurückgeben (x, y, Breite, Höhe);  

Nun wollen wir uns den Rest anschauen DrawPieces. Diese Methode ist etwas komplizierter als die DrawGrid aber es sollte immer noch ziemlich leicht zu verstehen sein. Sie durchlaufen jede Zeile und jede Spalte des Spielfelds, die im gespeichert sind Gitter Variable, und überprüfen Sie den Status dieses Rasterfelds. Wenn das Rasterfeld einen anderen Spieler als „Keiner“ enthält, zeichnen Sie die entsprechende Textur. Sie verwenden den ternären Operator, um die korrekte Textur basierend auf dem Zustand des Rasterbereichs zu erfassen und sie dann mit dem Rechteck zu zeichnen, das Sie erhalten haben GetGridSpace.

Die nächste Ausgabe, mit der Sie sich befassen, ist das Zeichnen des aktuellen Status. Der Status zeigt an, wer an der Reihe ist, wer das Spiel gewonnen hat oder ob das Spiel unentschieden ist. Füllen Sie die Methode wie folgt aus:

 private void DrawStatus () Texture2D-Textur; if (Gewinner! = TicTacToePlayer.None) Textur = Gewinner == TicTacToePlayer.PlayerO? oWinner: xWinner;  else if (! gewinnbar) texture = noWinner;  else texture = current == TicTacToePlayer.PlayerO? oTurn: xTurn;  Rectangle position = new Rectangle (spriteBatch.GraphicsDevice.Viewport.Width / 2 - (Textur.Width / 2), 15, Textur.Width, Textur.Height); spriteBatch.Draw (Textur, Position, Color.White);  

Diese Methode unterscheidet sich nicht wesentlich von den anderen von Ihnen erstellten Zeichenmethoden. Sie haben eine Textur und eine Position und müssen die Textur auf dem Bildschirm zeichnen. Der interessante Teil dieser Methode ist die Bestimmung der zu zeichnenden Textur. Sie prüfen zunächst, ob es einen Gewinner gibt. Wenn ja, wählen Sie die richtige Sieger-Textur und zeichnen diese. Dann prüfen Sie, ob alle Rasterfelder belegt sind und das Spiel nicht mehr gewinnbar ist. Wenn dies der Fall ist, wählen Sie die Textur ohne Gewinner und zeichnen diese. Wenn keine dieser Bedingungen zutrifft, prüfen Sie, welcher Spieler an der Reihe ist, wählen die richtige Ziehtextur und zeichnen diese. Wenn Sie Ihr Projekt zu diesem Zeitpunkt kompilieren und ausführen (durch Drücken von F5), sehen Sie, dass die Benutzeroberfläche anzeigt, dass O an der Reihe ist:

Zum Schluss erstellen Sie den Code, der die Rücksetztaste zeichnet. Das ist ziemlich einfach. Wenn es einen Gewinner gibt oder das Spiel nicht mehr gewinnbar ist, zeichnen Sie die Textur der Reset-Schaltfläche. Modifiziere den DrawResetButton Methode, so dass es wie folgt lautet:

 private void DrawResetButton () if (winner! = TicTacToePlayer.None ||! gewinnbar) spriteBatch.Draw (resetButton, resetButtonPosition, Color.White);  

An diesem Punkt haben Sie den gesamten Code erstellt, der zum Zeichnen der Benutzeroberfläche und aller zugehörigen Komponenten erforderlich ist. Jetzt müssen Sie nur noch die Aktualisierung Ihres Spiels basierend auf den Berührungen der Spieler durchführen.

Umgang mit Berührungen und Aktualisierungsstatus

Wenn Sie das letzte Lernprogramm für XNA befolgt haben, wird der folgende Code bekannt sein. Die Handhabung von Berührungen auf dem Bildschirm ist ziemlich normal und es ist nur innerhalb des Berührungshandhabungscodes, dass Sie Ihre Spielelogik haben. Lassen Sie uns zunächst den grundlegenden Code für die Handhabung von Berührungen in die Aktualisieren Methode. Finden Aktualisieren in deiner Game1 klassifizieren und wie folgt lesen:

 geschütztes überschreiben void Update (GameTime gameTime) if (GamePad.GetState (PlayerIndex.One) .Buttons.Back == ButtonState.Pressed) this.Exit ();  TouchCollection touches = TouchPanel.GetState (); if (! Berühren von && Berührungen.Zahl> 0) Berühren = true; TouchLocation touch = touches.First (); HandleBoardTouch (berühren); HandleResetTouch (touch);  else if (touches.Count == 0) Berühren = falsch;  base.Update (gameTime);  

Es gibt einige Dinge in dieser Methode, auf die Sie achten müssen. Zuerst wirst du ein neues bemerken berühren Variable, die nicht existiert Diese Variable speichert, ob der Spieler die Tafel auf der vorherigen Seite berührt hat oder nicht Aktualisieren aufrufen und verhindert, dass ein Finger mehrmals gespielt wird, ohne den Bildschirm zu verlassen. Hinzufügen berühren als Instanzvariable oben in Ihrer Klassendeklaration.

 bool berühren; 

Sie werden auch bemerken, dass Sie zwei Methodenaufrufe innerhalb von durchführen Aktualisieren Methode zu Methoden, die noch nicht existieren. Fügen Sie diese Methoden Ihrer Klassendeklaration direkt unter dem hinzu Aktualisieren Methode:

 private void HandleBoardTouch (TouchLocation touch)  private void HandleResetTouch (TouchLocation touch)  

Nun geh ich durch die Aktualisieren Methode, die Sie sehen, dass Sie auf dem Bildschirm nach aktuellen Berührungen suchen. Wenn Berührungen vorhanden sind und der Player den Bildschirm zuvor nicht berührt hat, greifen Sie zur ersten Berührung und geben Sie die Methode an die Methoden weiter, die die Berührungen der Platine behandeln und die Berührungen zurücksetzen. Wenn der Player den Bildschirm nicht berührt und sie zuvor waren, aktualisieren Sie die berühren Variable auf false.

Füllen Sie an dieser Stelle die aus HandleBoardTouch und HandleResetTouch Methoden. Diese entsprechen den Arbeitsablaufschritten 5 bzw. 6.

Umgang mit Platinenberührungen

Wenn ein Benutzer den Bildschirm berührt, muss das Spiel ein paar Dinge tun:

  • Prüfen Sie, ob sich die Berührung im Spielbereich befindet
  • Wenn es sich im Spielbereich befindet, prüfen Sie, ob der berührte Punkt bereits belegt ist
  • Wenn der Fleck ist nicht besetzt, dann legen Sie ein Stück dort ab und prüfen Sie, ob der aktuelle Spieler gewonnen hat
  • Wenn das gespielte Stück der Gewinner war, aktualisieren Sie den Status des Spiels entsprechend. Wenn das gespielte Stück den letzten verfügbaren Platz belegt hat, markieren Sie das Spiel als nicht gewinnbar
  • Tauschen Sie schließlich den aktuellen Spieler aus

Aktualisieren Sie die HandleBoardTouch Lesen Sie wie folgt und behandeln Sie alle obigen Schritte:

 private void HandleBoardTouch (TouchLocation touch) if (Gewinner == TicTacToePlayer.None) für (int i = 0; i < 3; i++)  for (int j = 0; j < 3; j++)  Rectangle box = GetGridSpace(i, j, 150, 150); if (grid[i, j] == TicTacToePlayer.None && box.Contains((int)touch.Position.X, (int)touch.Position.Y))  grid[i, j] = current; CheckForWin(current); CheckForWinnable(); current = current == TicTacToePlayer.PlayerO ? TicTacToePlayer.PlayerX : TicTacToePlayer.PlayerO;      

Wie Sie sehen, folgt diese Methode der oben beschriebenen grundlegenden Gliederung. Wenn es keinen Gewinner gibt, durchläuft es jedes Rasterfeld, überprüft, ob sich die Berührung in diesem Feld befindet, überprüft, ob das Feld offen ist, und legt ein Stück dort ab. Wenn Sie nach einem Gewinner suchen und sicherstellen, dass das Spiel immer noch gewinnbar ist, können Sie andere Methoden verwenden. Lassen Sie uns diese Methoden jetzt ausreden. Unter der HandleBoardTouch fügen Sie den folgenden Code hinzu:

 private void CheckForWin (TicTacToePlayer-Player)  private void CheckForWinnable ()  

Lassen Sie diese Methoden zunächst leer und kompilieren Sie Ihr Spiel. Berühren Sie die Platine, indem Sie auf den Emulator klicken, und beobachten Sie, wie sich die Statusmeldung ändert und die abgespielten Teile angezeigt werden. Du bist jetzt auf dem Weg zu einem kompletten Spiel!

Win-Bedingungen

An diesem Punkt gelangen Sie zum eigentlichen Fleisch des Spiels, der Gewinnlogik. Wie zuvor diskutiert, tritt ein Gewinnzustand auf, wenn die Steine ​​eines Spielers eine der Reihen, eine der Spalten oder eine der beiden Diagonalen besetzen. Das Spiel kann nicht gewonnen werden, wenn kein Gewinner bekannt ist und keine freien Plätze übrig sind. Für dieses Spiel verwenden wir ein mehrdimensionales Array, um den Zustand des Gitters zu speichern. C # bietet keine Kontrollmethoden für Zeilen, Spalten oder Diagonalen. Glücklicherweise unterstützt die Sprache eine fantastische Funktion, die als Erweiterungsmethoden bezeichnet wird, die Sie hier verwenden, um Ihren Code ein wenig sauberer zu machen.

Helfer erstellen

Um die Erweiterungsmethoden zu erstellen, müssen Sie zuerst eine neue Klasse erstellen. Klicken Sie im Projektmappen-Explorer auf den Namen Ihres Projekts und klicken Sie auf „Hinzufügen> Klasse…“. Benennen Sie Ihre Klasse MultiDimensionalArrayExtensions und klicken Sie auf "Hinzufügen". Wenn Ihre neue Datei zum Bearbeiten geöffnet wird, ändern Sie sie so, dass Ihre Klassendeklaration folgendermaßen aussieht:

 öffentliche statische Klasse MultiDimensionalArrayExtensions  

Sie werden sehen, dass Sie die Modifikatoren hinzugefügt haben Öffentlichkeit und statisch zur Klassendeklaration. Dies ist zum Erstellen von Erweiterungsmethoden erforderlich. Nun werden wir ein paar Methoden entwickeln, die wir brauchen, von denen jede eine zurückgibt IEnumerable zur einfachen Abfrage:

  • Zeile - Gibt alle Elemente in einer bestimmten Zeile zurück
  • Column - Gibt alle Elemente in einer bestimmten Spalte zurück
  • Diagonale - Gibt alle Elemente in einer Diagonale zurück
  • Alle - Gibt alle Elemente im mehrdimensionalen Array zurück

Ändern Sie Ihre Klasse erneut und fügen Sie die Methoden wie folgt hinzu:

 öffentliche statische Klasse MultiDimensionalArrayExtensions public static IEnumerable Reihe(dieses T [,] Array, int row) var columnLower = array.GetLowerBound (1); var columnUpper = array.GetUpperBound (1); for (int i = columnLower; i <= columnUpper; i++)  yield return array[row, i];   public static IEnumerable Säule(dieses T [,] Array, int Spalte) var rowLower = array.GetLowerBound (0); var rowUpper = array.GetUpperBound (0); für (int i = rowLower; i <= rowUpper; i++)  yield return array[i, column];   public static IEnumerable Diagonal(dieses T [,] - Array, DiagonalDirection-Richtung) var rowLower = array.GetLowerBound (0); var rowUpper = array.GetUpperBound (0); var columnLower = array.GetLowerBound (1); var columnUpper = array.GetUpperBound (1); for (int row = rowLower, column = columnLower; row) <= rowUpper && column <= columnUpper; row++, column++)  int realColumn = column; if (direction == DiagonalDirection.DownLeft) realColumn = columnUpper - columnLower - column; yield return array[row, realColumn];   public enum DiagonalDirection  DownRight, DownLeft  public static IEnumerable Alles(dieses T [,] -Array) var rowLower = array.GetLowerBound (0); var rowUpper = array.GetUpperBound (0); var columnLower = array.GetLowerBound (1); var columnUpper = array.GetUpperBound (1); for (int row = rowLower; row) <= rowUpper; row++)  for (int column = columnLower; column <= columnUpper; column++)  yield return array[row, column];     

Sie können sehen, dass es bei diesen Methoden nicht viel gibt. Für jeden von ihnen wird ein bestimmter Teil des mehrdimensionalen Arrays iteriert und dieser Teil des Arrays wird als Teil eines zurückgegeben IEnumerable. Der einzig wirklich knifflige Teil kann die Verwendung der sein Ausbeute Stichwort. Eine Erläuterung des Verhaltens dieses Schlüsselworts liegt außerhalb des Umfangs dieses Artikels. Die C # -Referenz auf MSDN für das Fließschlüsselwort kann hilfreich sein, wenn Sie mehr erfahren möchten. Als Nebenbemerkung wird ein Großteil der Arbeit an diesen Erweiterungsmethoden einem Benutzerbeitrag zu StackOverflow entnommen, den Sie hier finden

Implementieren der Win-Logik

Nun, da die erforderlichen Erweiterungsmethoden implementiert sind, sollte es ziemlich einfach sein, die Win-Logik zu implementieren. Gehen wir zurück zum CheckForWin Methode und implementieren Sie es. Aktualisieren Sie die Methode wie folgt:

 private void CheckForWin (TicTacToePlayer-Player) Func checkWinner = b => b == Spieler; if (grid.Row (0) .All (checkWinner) || grid.Row (1) .All (checkWinner) || grid.Row (2) .All (checkWinner) || grid.Column (0) .All ( checkWinner) || grid.Column (1) .All (checkWinner) || grid.Column (2) .All (checkWinner) || grid.Diagonal (MultiDimensionalArrayExtensions.DiagonalDirection.DownRight) .All (checkWinner) || grid.Diagonal (MultiDimensionalArrayExtensions.DiagonalDirection.DownLeft) .All (checkWinner)) winner = player;  

In Anbetracht dessen, was Sie bereits von der Erstellung der Erweiterungsmethoden wissen, sollte dies ziemlich einfach zu entschlüsseln sein. Sie erstellen zuerst eine "Func": http: //msdn.microsoft.com/de-de/library/bb549151.aspx Objekt, das als Delegat fungiert und die Verwendung einer Lambda-Anweisung (das b => b == Spieler Teil) eine Abfrage IEnumerable (wie diejenigen, die von der Erweiterungsmethode zurückgegeben wurden) und geben Sie a zurück bool. Sie wenden das dann an Func Objekt über jede Zeile, Spalte und Diagonale mit der IEnumerable.All Methode. Wenn einer dieser Fälle zutrifft, weisen Sie das zu Gewinner Instanzvariable für die Spieler Parameter. Wenn keiner dieser Fälle zutrifft, passiert nichts.

Ändern Sie jetzt Ihre CheckForWinnable Methode:

 private void CheckForWinnable () if (Gewinner == TicTacToePlayer.None) Func checkNone = b => b == TicTacToePlayer.None; if (! grid.All (). Any (checkNone)) winnable = false;  

Diese Methode ist sehr ähnlich CheckForWin. Zuerst prüfen Sie, ob das Spiel einen Gewinner hat. Wenn nicht, erstellen Sie eine Func Objekt, das prüft, ob ein Element dem entspricht TicTacToePlayer Keiner. Sie wenden das dann an Func gegen alle Leerzeichen im Raster und prüfen, ob eines der Leerzeichen frei ist. Ist dies nicht der Fall, kann das Spiel nicht mehr gewonnen werden, und Sie können die Instanzvariable umschalten gewinnbar.

Beenden

An diesem Punkt ist dein Spiel einsatzbereit. Sie können Ihr Projekt kompilieren und ausführen, indem Sie F5 drücken und mit dem Spielen beginnen (entweder von Ihnen selbst oder mit einem Partner). Wechseln Sie abwechselnd Teile auf das Brett, beobachten Sie, wie sich die Statusmeldung ändert, und sehen Sie, was passiert, wenn Sie gewinnen. Sobald Sie gewonnen oder unentschieden gespielt haben, klicken Sie auf die Schaltfläche zum Zurücksetzen und beobachten Sie, wie das Spiel in seinen ursprünglichen Zustand zurückkehrt.

An dieser Stelle gibt es verschiedene Möglichkeiten, die Sie tun können. Sie können einen Gewinnzähler implementieren, der angibt, wie oft jeder Spieler gewonnen hat. Sie können die Darstellung der Teile ändern oder eine coole Animation hinzufügen, wenn ein Gewinner bekannt gegeben wird. Sie könnten das Spiel thematisieren, um es ein wenig interessanter zu machen, indem Sie die Rebellenallianz gegen das galaktische Imperium antreten?

An diesem Punkt müssen Sie alles erweitern und entwickeln, was Sie möchten. Ich hoffe, Sie haben dieses Tutorial so gerne verfolgt, wie ich es geschrieben habe, und freuen uns auf Ihre Kommentare.