Dieser Beitrag aus unserem Archiv führt Sie durch die Schritte zur Erstellung eines Tic Tac Toe-Spiels in React. Beachten Sie jedoch, dass die verwendeten Bibliotheksversionen nicht mehr die neuesten sind. Insbesondere verwendet dieser Artikel die Version 1 unseres React SDKs, aber alle unten aufgeführten Schritte und Prinzipien sind immer noch gültig
Tic Tac Toe ist ein typisches Kinderspiel. Alles, was es braucht, ist etwas zum Schreiben und etwas, mit dem man schreiben kann. Was aber, wenn Sie mit jemandem spielen möchten, der sich an einem anderen Ort befindet? In diesem Fall müssen Sie eine Anwendung verwenden, die Sie und einen anderen Spieler mit dem Spiel verbindet.
Die Anwendung muss ein Echtzeit-Erlebnis bieten, so dass jeder Zug, den Sie machen, sofort von dem anderen Spieler gesehen wird und andersherum. Wenn die Anwendung dieses Erlebnis nicht bietet, werden Sie und viele andere sie wahrscheinlich nicht mehr nutzen.
Wie kann ein Entwickler also eine vernetzte Erfahrung bieten, bei der die Spieler Tic Tac Toe oder ein anderes Spiel spielen können, egal wo sie sich auf der Welt befinden?
Konzepte für Echtzeit-Multiplayer-Spiele
Es gibt mehrere Möglichkeiten, die Echtzeit-Infrastruktur für Multiplayer-Spiele bereitzustellen. Sie können Ihre eigene Infrastruktur von Grund auf aufbauen, indem Sie Technologien und Open-Source-Protokolle wie Socket.IO, SignalR oder WebSockets verwenden.
Dieser Weg mag zwar verlockend erscheinen, aber Sie werden dabei auf mehrere Probleme stoßen; ein solches Problem ist die Skalierbarkeit. Es ist nicht schwer, mit 100 Benutzern umzugehen, aber wie können Sie 100.000+ Benutzer verwalten? Neben den Problemen mit der Infrastruktur müssen Sie sich auch noch um die Wartung Ihres Spiels kümmern.
Letzten Endes ist das Einzige, was zählt, ein großartiges Spielerlebnis für die Spieler. Aber wie löst man das Infrastrukturproblem? An dieser Stelle kommt PubNub ins Spiel.
PubNub bietet die Echtzeit-Infrastruktur, um jede Anwendung über sein globales Data Stream Network zu betreiben. Mit mehr als 70 SDKs, einschließlich der beliebtesten Programmiersprachen, vereinfacht PubNub das Senden und Empfangen von Nachrichten an jedes Gerät in weniger als 100 ms. PubNub ist sicher, skalierbar und zuverlässig, so dass Sie sich nicht um die Erstellung und Wartung Ihrer eigenen Infrastruktur kümmern müssen.
Um zu zeigen, wie einfach es ist, ein Multiplayer-Spiel mit PubNub zu entwickeln, werden wir ein einfaches React Tic Tac Toe-Spiel mit dem PubNub React SDK erstellen. In diesem Spiel verbinden sich zwei Spieler mit einem eigenen Spielkanal, in dem sie gegeneinander antreten. Jeder Zug, den ein Spieler macht, wird auf dem Kanal veröffentlicht, um das Spielbrett des anderen Spielers in Echtzeit zu aktualisieren.
App-Übersicht
So wird unsere App aussehen, wenn wir fertig sind.
Die Spieler betreten zunächst die Lobby, wo sie einen Kanal erstellen oder einem Kanal beitreten können. Wenn der Spieler einen Kanal erstellt, erhält er eine Raumnummer, die er mit einem anderen Spieler teilen kann. Der Spieler, der den Kanal erstellt hat, wird Spieler X und macht den ersten Zug, wenn das Spiel beginnt.
Der Spieler, der einem Channel mit der ihm zugewiesenen Raumnummer beitritt, wird Spieler O. Spieler können nur Channels beitreten, wenn sich eine weitere Person in dem Channel befindet. Wenn mehr als eine Person anwesend ist, läuft ein Spiel in diesem Channel und der Spieler kann nicht beitreten. Das Spiel beginnt, sobald sich zwei Spieler im Channel befinden.
Am Ende des Spiels wird der Punktestand des Gewinners um einen Punkt erhöht. Wenn das Spiel unentschieden endet, erhält keiner der beiden Spieler einen Punkt. Spieler X wird in einem Modal gefragt, ob er eine neue Runde beginnen oder das Spiel beenden möchte. Wenn Spieler X das Spiel fortsetzt, wird das Spielbrett für die neue Runde zurückgesetzt. Andernfalls endet das Spiel und beide Spieler kehren in die Lobby zurück.
Einrichten der Lobby
Bevor wir die Lobby einrichten, melden Sie sich für ein kostenloses PubNub-Konto an, um Ihre kostenlosen Pub/Sub-API-Schlüssel über das PubNub-Administrations-Dashboard zu erhalten.
Sobald Sie Ihre Schlüssel erhalten haben, fügen Sie sie in den Konstruktor von App.js ein.
// App.js
import React, { Component } from 'react';
import Game from './Game';
import Board from './Board';
import PubNubReact from 'pubnub-react';
import Swal from "sweetalert2";
import shortid from 'shortid';
import './Game.css';
class App extends Component {
constructor(props) {
super(props);
// REPLACE with your keys
this.pubnub = new PubNubReact({
publishKey: "YOUR_PUBLISH_KEY_HERE",
subscribeKey: "YOUR_SUBSCRIBE_KEY_HERE"
});
this.state = {
piece: '', // X or O
isPlaying: false, // Set to true when 2 players are in a channel
isRoomCreator: false,
isDisabled: false,
myTurn: false,
};
this.lobbyChannel = null; // Lobby channel
this.gameChannel = null; // Game channel
this.roomId = null; // Unique id when player creates a room
this.pubnub.init(this); // Initialize PubNub
}
render() {
return ();
}
}
export default App;
Ebenfalls im Konstruktor werden die Statusobjekte und Variablen initialisiert. Wir werden auf die Objekte und Variablen eingehen, wenn sie in der Datei auftauchen. Schließlich haben wir PubNub am Ende des Konstruktors initialisiert.
Innerhalb der Render-Methode und der Rückgabeanweisung fügen wir das Markup für die Lobby-Komponente hinzu.
return (
<div>
<div className="title">
<p> React Tic Tac Toe </p>
</div>
{
!this.state.isPlaying &&
<div className="game">
<div className="board">
<Board
squares={0}
onClick={index => null}
/>
<div className="button-container">
<button
className="create-button "
disabled={this.state.isDisabled}
onClick={(e) => this.onPressCreate()}
> Create
</button>
<button
className="join-button"
onClick={(e) => this.onPressJoin()}
> Join
</button>
</div>
</div>
</div>
}
{
this.state.isPlaying &&
<Game
pubnub={this.pubnub}
gameChannel={this.gameChannel}
piece={this.state.piece}
isRoomCreator={this.state.isRoomCreator}
myTurn={this.state.myTurn}
xUsername={this.state.xUsername}
oUsername={this.state.oUsername}
endGame={this.endGame}
/>
}
</div>
);
Die Lobby-Komponente besteht aus einem Titel, einem leeren Tic-Tac-Toe-Brett (es passiert nichts, wenn der Spieler auf die Quadrate drückt) und den Schaltflächen "Erstellen_"_ und "Beitreten_". Diese Komponente wird nur angezeigt, wenn der Statuswert _isPlaying falsch ist. Wenn er auf true gesetzt ist, hat das Spiel begonnen und die Komponente wird in die Komponente Game umgewandelt, die wir im zweiten Teil des Tutorials besprechen werden.
Die Board-Komponente ist ebenfalls Teil der Lobby-Komponente. Innerhalb der Board-Komponente befindet sich die Square-Komponente. Wir werden nicht näher auf diese beiden Komponenten eingehen, um uns auf die Komponenten Lobby und Spiel zu konzentrieren.
Wenn der Spieler auf die Schaltfläche "Erstellen" drückt, wird die Schaltfläche deaktiviert, damit der Spieler nicht mehrere Kanäle erstellen kann. Die Schaltfläche "Beitreten" ist nicht deaktiviert, für den Fall, dass der Spieler stattdessen einem Channel beitreten möchte. Sobald die Schaltfläche "Erstellen" gedrückt wird, wird die Methode onPressCreate() aufgerufen.
Einen Kanal erstellen
Das erste, was wir in onPressCreate() tun, ist, eine zufällige, auf 5 Zeichen gekürzte String-ID zu generieren. Dazu verwenden wir shortid(). Die Zeichenkette wird an 'tictactoelobby-'_angehängt,_was der eindeutige Lobby-Kanal sein wird, den die Spieler abonnieren.
// Create a room channel
onPressCreate = (e) => {
// Create a random name for the channel
this.roomId = shortid.generate().substring(0,5);
this.lobbyChannel = 'tictactoelobby--' + this.roomId; // Lobby channel name
this.pubnub.subscribe({
channels: [this.lobbyChannel],
withPresence: true // Checks the number of people in the channel
});
}
Um zu verhindern, dass mehr als zwei Spieler einem bestimmten Kanal beitreten, verwenden wir Presence. Später werden wir uns die Logik zur Überprüfung der Belegung des Kanals ansehen.
Sobald der Spieler den Lobbykanal abonniert hat, wird ein Modal mit der Raum-ID angezeigt, damit ein anderer Spieler diesem Kanal beitreten kann.
Dieses Modal und alle in dieser Anwendung verwendeten Modals werden von SweetAlert2 erstellt, um die standardmäßigen alert()-Popup-Boxen von JavaScript zu ersetzen.
// Inside of onPressCreate()
// Modal
Swal.fire({
position: 'top',
allowOutsideClick: false,
title: 'Share this room ID with your friend',
text: this.roomId,
width: 275,
padding: '0.7em',
// Custom CSS to change the size of the modal
customClass: {
heightAuto: false,
title: 'title-class',
popup: 'popup-class',
confirmButton: 'button-class'
}
})
Am Ende von onPressCreate() ändern wir die Statuswerte, um den neuen Status der App wiederzugeben.
this.setState({
piece: 'X',
isRoomCreator: true,
isDisabled: true, // Disable the 'Create' button
myTurn: true, // Player X makes the 1st move
});
Sobald der Spieler einen Raum erstellt hat, muss er warten, bis ein anderer Spieler diesem Raum beitritt. Schauen wir uns die Logik für den Beitritt zu einem Raum an.
Einem Kanal beitreten
Wenn ein Spieler auf die Schaltfläche 'Beitreten' drückt, wird ein Aufruf von onPressJoin() ausgeführt. Dem Spieler wird ein Modal angezeigt, in dem er aufgefordert wird, die Raum-ID in das Eingabefeld einzugeben.
Wenn der Spieler die Raumkennung eingibt und die Schaltfläche 'OK' drückt, wird joinRoom(value) aufgerufen, wobei value die Raumkennung ist. Diese Methode wird nicht aufgerufen, wenn das Eingabefeld leer ist oder wenn der Spieler die Schaltfläche "Abbrechen" drückt.
// The 'Join' button was pressed
onPressJoin = (e) => {
Swal.fire({
position: 'top',
input: 'text',
allowOutsideClick: false,
inputPlaceholder: 'Enter the room id',
showCancelButton: true,
confirmButtonColor: 'rgb(208,33,41)',
confirmButtonText: 'OK',
width: 275,
padding: '0.7em',
customClass: {
heightAuto: false,
popup: 'popup-class',
confirmButton: 'join-button-class',
cancelButton: 'join-button-class'
}
}).then((result) => {
// Check if the user typed a value in the input field
if(result.value){
this.joinRoom(result.value);
}
})
}
Das erste, was wir in joinRoom() tun, ist, den Wert an 'tictactoelobby-'_anzuhängen,_ähnlich wie wir es in onPressCreate() getan haben.
// Join a room channel
joinRoom = (value) => {
this.roomId = value;
this.lobbyChannel = 'tictactoelobby--' + this.roomId;
}
Bevor der Spieler den Lobby-Kanal abonniert, müssen wir die Gesamtbelegung des Kanals mit hereNow() überprüfen. Wenn die Gesamtbelegung weniger als 2 beträgt, kann der Spieler den Lobby-Kanal erfolgreich abonnieren.
// Check the number of people in the channel
this.pubnub.hereNow({
channels: [this.lobbyChannel],
}).then((response) => {
if(response.totalOccupancy < 2){
this.pubnub.subscribe({
channels: [this.lobbyChannel],
withPresence: true
});
this.setState({
piece: 'O', // Player O
});
this.pubnub.publish({
message: {
notRoomCreator: true,
},
channel: this.lobbyChannel
});
}
}).catch((error) => {
console.log(error);
});
Nachdem der Spieler den Lobby-Kanal abonniert hat, wird der Statuswert von piece auf 'O' geändert und eine Nachricht an diesen Lobby-Kanal veröffentlicht. Diese Nachricht benachrichtigt den Spieler X, dass ein anderer Spieler dem Kanal beigetreten ist. Wir richten den Nachrichten-Listener in componentDidUpdate() ein, zu dem wir gleich kommen werden.
Wenn die Gesamtbelegung größer als 2 ist, ist ein Spiel im Gange und der Spieler, der versucht, dem Kanal beizutreten, wird abgewiesen. Der folgende Code befindet sich unterhalb der if-Anweisung in hereNow().
// Below the if statement in hereNow()
else{
// Game in progress
Swal.fire({
position: 'top',
allowOutsideClick: false,
title: 'Error',
text: 'Game in progress. Try another room.',
width: 275,
padding: '0.7em',
customClass: {
heightAuto: false,
title: 'title-class',
popup: 'popup-class',
confirmButton: 'button-class'
}
})
}
Werfen wir nun einen Blick auf componentDidUpdate().
Starten Sie das Spiel
In componentDidUpdate() prüfen wir, ob der Spieler mit einem Kanal verbunden ist, d. h. wir prüfen, ob this.lobbyChannel nicht null ist. Wenn dies nicht der Fall ist, richten wir einen Listener ein, der auf alle Nachrichten hört, die auf dem Kanal ankommen.
componentDidUpdate() {
// Check that the player is connected to a channel
if(this.lobbyChannel != null){
this.pubnub.getMessage(this.lobbyChannel, (msg) => {
// Start the game once an opponent joins the channel
if(msg.message.notRoomCreator){
// Create a different channel for the game
this.gameChannel = 'tictactoegame--' + this.roomId;
this.pubnub.subscribe({
channels: [this.gameChannel]
});
}
});
}
}
Wir prüfen, ob die Nachricht msg.message.notRoomCreator ist, die von dem Spieler veröffentlicht wird, der dem Kanal beitritt. Wenn ja, erstellen wir einen neuen Kanal, 'tictactoegame-',_mit der _Raum-ID an die Zeichenkette angehängt. Der Spielkanal wird verwendet, um alle Züge der Spieler zu veröffentlichen, die ihre Spielbretter aktualisieren.
Schließlich wird nach dem Abonnieren des Spielkanals der Statuswert von isPlaying auf true gesetzt. Dadurch wird die Lobbykomponente durch die Spielkomponente ersetzt.
this.setState({
isPlaying: true
});
// Close the modals if they are opened
Swal.close();
}
Sobald die Spielkomponente angezeigt wird, wollen wir alle Modals, sofern sie geöffnet sind, von der Lobby-Komponente aus schließen, indem wir Swal.close() ausführen.
Jetzt haben wir zwei Spieler, die mit einem einzigen Spielkanal verbunden sind, und sie können anfangen, Tic Tac Toe zu spielen! Im nächsten Abschnitt werden wir die Benutzeroberfläche und die Logik für die Spielkomponente implementieren.
Spielfunktionen erstellen
Das erste, was wir in Game.js tun, ist, den Basiskonstruktor einzurichten:
// Game.js
import React from 'react';
import Board from './Board';
import Swal from "sweetalert2";
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(''), // 3x3 board
xScore: 0,
oScore: 0,
whosTurn: this.props.myTurn // Player X goes first
};
this.turn = 'X';
this.gameOver = false;
this.counter = 0; // Game ends in a tie when counter is 9
}
render() {
return ();
}
}
export default Game;
Für die Zustandsobjekte initialisieren wir die Eigenschaft array squares, die dazu dient, die Positionen der Spieler auf dem Spielbrett zu speichern. Dies wird weiter unten erklärt. Außerdem setzen wir den Punktestand der Spieler auf 0 und den Wert von whosTurn auf myTurn, der für Spieler X auf true und für Spieler O auf false initialisiert wird.
Der Wert der Variablen turn und counter ändert sich im Laufe des Spiels. Am Ende des Spiels wird gameOver auf true gesetzt.
Hinzufügen der Benutzeroberfläche
Als Nächstes richten wir das Markup für die Game-Komponente innerhalb der Render-Methode ein.
render() {
let status;
// Change to current player's turn
status = `${this.state.whosTurn ? "Your turn" : "Opponent's turn"}`;
return (
<div className="game">
<div className="board">
<Board
squares={this.state.squares}
onClick={index => this.onMakeMove(index)}
/>
<p className="status-info">{status}</p>
</div>
<div className="scores-container">
<div>
<p>Player X: {this.state.xScore} </p>
</div>
<div>
<p>Player O: {this.state.oScore} </p>
</div>
</div>
</div>
);
}
Wir zeigen den Wert des Status in der UI an, um den Spielern mitzuteilen, ob sie an der Reihe sind, einen Zug zu machen, oder ob der andere Spieler am Zug ist. Der boolesche Wert des Status whosTurn wird jedes Mal aktualisiert, wenn ein Zug gemacht wird. Der Rest der Benutzeroberfläche besteht aus der Brettkomponente und dem Punktestand des Spielers.
Hinzufügen der Logik
Wenn der Spieler einen Zug auf dem Brett macht, erfolgt ein Aufruf von onMakeMove(index), wobei index die Position ist, an der die Figur auf dem Brett steht. Das Spielbrett hat 3 Reihen und 3 Spalten, also insgesamt 9 Felder. Jedes Feld hat seinen eigenen Indexwert, beginnend mit dem Wert 0 und endend mit dem Wert 8.
onMakeMove = (index) =>{
const squares = this.state.squares;
// Check if the square is empty and if it's the player's turn to make a move
if(!squares[index] && (this.turn === this.props.piece)){
squares[index] = this.props.piece;
this.setState({
squares: squares,
whosTurn: !this.state.whosTurn
});
// Other player's turn to make a move
this.turn = (this.turn === 'X') ? 'O' : 'X';
// Publish move to the channel
this.props.pubnub.publish({
message: {
index: index,
piece: this.props.piece,
turn: this.turn
},
channel: this.props.gameChannel
});
// Check if there is a winner
this.checkForWinner(squares)
}
}
Nachdem der Zustand der Felder ermittelt wurde, wird eine bedingte Anweisung verwendet, um zu prüfen, ob das Feld, das der Spieler berührt hat, leer ist und ob er an der Reihe ist, einen Zug zu machen. Wenn eine oder beide Bedingungen nicht erfüllt sind, wird die Figur des Spielers nicht auf das Feld gesetzt. Andernfalls wird die Figur des Spielers zu den Feldern im Array hinzugefügt, die dem Index entsprechen, auf dem die Figur platziert wurde.
Wenn Spieler X zum Beispiel einen Zug in Zeile 0, Spalte 2 macht und die Bedingung erfüllt ist, dann haben die Felder[2] den Wert "X".Als nächstes wird der Status geändert, um den neuen Zustand des Spiels widerzuspiegeln, und der Zug wird aktualisiert, damit der andere Spieler seinen Zug machen kann. Damit das Brett des anderen Spielers mit den aktuellen Daten aktualisiert wird, veröffentlichen wir die Daten im Spielkanal. All dies geschieht in Echtzeit, so dass beide Spieler sofort sehen, dass ihre Spielbretter aktualisiert werden, sobald ein gültiger Zug gemacht wird. Der letzte Schritt in dieser Methode ist der Aufruf checkForWinner(squares), um zu prüfen, ob es einen Gewinner gibt.
Bevor wir das tun, werfen wir einen Blick auf componentDidMount() , wo wir den Listener für neue Nachrichten einrichten, die im Spielkanal ankommen.
componentDidMount(){
this.props.pubnub.getMessage(this.props.gameChannel, (msg) => {
// Update other player's board
if(msg.message.turn === this.props.piece){
this.publishMove(msg.message.index, msg.message.piece);
}
});
}
Da beide Spieler mit demselben Spielkanal verbunden sind, werden beide diese Nachricht erhalten. Die Methode publishMove(index, piece) wird aufgerufen, wobei index die Position ist, an der die Figur platziert wurde, und piece die Figur des Spielers ist, der den Zug gemacht hat. Diese Methode aktualisiert das Brett mit dem aktuellen Zug und prüft, ob es einen Gewinner gibt. Um zu verhindern, dass der Spieler, der den aktuellen Zug ausgeführt hat, diesen Vorgang erneut durchführen muss, prüft die if-Anweisung, ob die Figur des Spielers dem Wert von turn entspricht. Ist dies der Fall, wird das Brett aktualisiert.
// Opponent's move is published to the board
publishMove = (index, piece) => {
const squares = this.state.squares;
squares[index] = piece;
this.turn = (squares[index] === 'X')? 'O' : 'X';
this.setState({
squares: squares,
whosTurn: !this.state.whosTurn
});
this.checkForWinner(squares)
}
Die Logik der Aktualisierung des Spielbretts ist die gleiche wie bei onMakeMove(). Lassen Sie uns nun checkForWinner() durchgehen.
checkForWinner = (squares) => {
// Possible winning combinations
const possibleCombinations = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
// Iterate every combination to see if there is a match
for (let i = 0; i < possibleCombinations.length; i += 1) {
const [a, b, c] = possibleCombinations[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
this.announceWinner(squares[a]);
return;
}
}
}
Alle Gewinnkombinationen befinden sich in dem doppelten Array possibleCombinations, wobei jedes Array eine mögliche Kombination ist, um das Spiel zu gewinnen. Jedes Array in possibleCombinations wird mit dem Array squares abgeglichen. Wenn es eine Übereinstimmung gibt, dann gibt es einen Gewinner. Um dies zu verdeutlichen, wollen wir ein Beispiel verwenden.
Angenommen, Spieler X macht einen Gewinnzug in Reihe 2 Spalte 0. Der Index dieser Position ist 6. Das Brett sieht nun wie folgt aus:
Die Gewinnkombination für Spieler X ist [2,4,6]. Das Feld Quadrate wird aktualisiert zu: ["O", "", "X", "O", "X", "", "X", "", ""].
Wenn [a,b,c] in der for-Schleife die Werte [2,4,6] hat, ist die if-Anweisung in der for-Schleife wahr, da [2,4,6] alle den gleichen Wert von X haben. Der Punktestand des Gewinners muss aktualisiert werden, also wird*announceWinner()* aufgerufen, um den Gewinner zu ermitteln.
Wenn das Spiel mit einem Unentschieden endet, gibt es für diese Runde keinen Gewinner. Um zu prüfen, ob ein Spiel unentschieden endet, verwenden wir einen Zähler, der bei jedem Zug auf dem Brett um eins erhöht wird.
// Below the for loop in checkForWinner()
// Check if the game ends in a draw
this.counter++;
// The board is filled up and there is no winner
if(this.counter === 9){
this.gameOver = true;
this.newRound(null);
}
Erreicht der Zähler den Wert 9, endet das Spiel unentschieden, weil der Spieler auf dem letzten Feld des Brettes keinen Gewinnzug gemacht hat. In diesem Fall wird die Methode newRound() mit einem Null-Argument aufgerufen, da es keinen Gewinner gibt.
Bevor wir zu dieser Methode gehen, lassen Sie uns zu*announceWinner()* zurückkehren.
// Update score for the winner
announceWinner = (winner) => {
let pieces = {
'X': this.state.xScore,
'O': this.state.oScore
}
if(winner === 'X'){
pieces['X'] += 1;
this.setState({
xScore: pieces['X']
});
}
else{
pieces['O'] += 1;
this.setState({
oScore: pieces['O']
});
}
// End the game once there is a winner
this.gameOver = true;
this.newRound(winner);
}
Der Parameter dieser Methode ist winner, d.h. der Spieler, der das Spiel gewonnen hat. Wir prüfen, ob der Gewinner 'X' oder 'O' ist und erhöhen den Punktestand des Gewinners um einen Punkt. Da das Spiel beendet ist, wird die Variable gameOver auf true gesetzt und die Methode newRound() aufgerufen.
Eine neue Runde beginnen
Spieler X hat die Möglichkeit, eine weitere Runde zu spielen oder das Spiel zu beenden und zurück in die Lobby zu gehen.
Der andere Spieler hat gesagt, dass er warten soll, bis Spieler X entscheidet, was er tun will.
Sobald Spieler X entschieden hat, was er tun will, wird eine Nachricht im Spielkanal veröffentlicht, um den anderen Spieler zu informieren. Die Benutzeroberfläche wird dann aktualisiert.
newRound = (winner) => {
// Announce the winner or announce a tie game
let title = (winner === null) ? 'Tie game!' : `Player ${winner} won!`;
// Show this to Player O
if((this.props.isRoomCreator === false) && this.gameOver){
Swal.fire({
position: 'top',
allowOutsideClick: false,
title: title,
text: 'Waiting for a new round...',
confirmButtonColor: 'rgb(208,33,41)',
width: 275,
customClass: {
heightAuto: false,
title: 'title-class',
popup: 'popup-class',
confirmButton: 'button-class',
} ,
});
this.turn = 'X'; // Set turn to X so Player O can't make a move
}
// Show this to Player X
else if(this.props.isRoomCreator && this.gameOver){
Swal.fire({
position: 'top',
allowOutsideClick: false,
title: title,
text: 'Continue Playing?',
showCancelButton: true,
confirmButtonColor: 'rgb(208,33,41)',
cancelButtonColor: '#aaa',
cancelButtonText: 'Nope',
confirmButtonText: 'Yea!',
width: 275,
customClass: {
heightAuto: false,
title: 'title-class',
popup: 'popup-class',
confirmButton: 'button-class',
cancelButton: 'button-class'
} ,
}).then((result) => {
// Start a new round
if (result.value) {
this.props.pubnub.publish({
message: {
reset: true
},
channel: this.props.gameChannel
});
}
else{
// End the game
this.props.pubnub.publish({
message: {
endGame: true
},
channel: this.props.gameChannel
});
}
})
}
}
Wenn die Nachricht zurückgesetzt wird, werden alle Statuswerte und Variablen, mit Ausnahme des Punktestands der Spieler, auf ihre ursprünglichen Werte zurückgesetzt. Alle noch offenen Modals werden geschlossen und eine neue Runde beginnt für beide Spieler.
Bei der Nachricht endGame werden alle Modals geschlossen und die Methode endGame() wird aufgerufen. Diese Methode befindet sich in App.js.
// Reset everything
endGame = () => {
this.setState({
piece: '',
isPlaying: false,
isRoomCreator: false,
isDisabled: false,
myTurn: false,
});
this.lobbyChannel = null;
this.gameChannel = null;
this.roomId = null;
this.pubnub.unsubscribe({
channels : [this.lobbyChannel, this.gameChannel]
});
}
Alle Statuswerte und Variablen werden auf ihre Anfangswerte zurückgesetzt. Die Kanalnamen werden auf Null zurückgesetzt, da jedes Mal, wenn ein Spieler einen Raum erstellt, ein neuer Name generiert wird. Da die Kanalnamen nicht mehr nützlich sein werden, melden sich die Spieler sowohl von der Lobby als auch vom Spielkanal ab. Der Wert von isPlaying wird auf false zurückgesetzt, so dass die Spielkomponente durch die Lobbykomponente ersetzt wird.
Die letzte Methode, die in App.js aufgenommen werden muss, ist componentWillUnmount(), die die Spieler von beiden Kanälen abmeldet.
componentWillUnmount() {
this.pubnub.unsubscribe({
channels : [this.lobbyChannel, this.gameChannel]
});
}
Das ist alles, was wir tun müssen, damit das Spiel funktioniert! Die CSS-Datei für das Spiel ist im Repo zu finden. Nun wollen wir das Spiel zum Laufen bringen.
Das Spiel starten
Bevor wir das Spiel starten können, müssen wir noch ein paar kleine Schritte durchführen. Als erstes müssen wir das Presence-Feature aktivieren, da wir es benutzen, um die Anzahl der Personen im Channel zu ermitteln (wir haben withPresence benutzt, als wir den Lobby-Channel abonniert haben). Gehe zum PubNub Admin Dashboard und klicke auf deine Anwendung. Klicken Sie auf Keyset und scrollen Sie nach unten zu Application add-ons. Schalten Sie den Schalter Presence auf on. Behalten Sie die Standardwerte bei.
Um die drei in der Anwendung verwendeten Abhängigkeiten zu installieren und die Anwendung auszuführen, können Sie das Skript dependencies.sh ausführen, das sich im Stammverzeichnis der Anwendung befindet.
# dependencies.sh
npm install --save pubnub pubnub-react
npm install --save shortid
npm install --save sweetalert2
npm start
Wechseln Sie im Terminal in das Stammverzeichnis der App und geben Sie den folgenden Befehl ein, um das Skript ausführbar zu machen:
chmod +x dependencies.sh
Führen Sie das Skript mit diesem Befehl aus:
./dependencies.sh
Die Anwendung wird in http://localhost:3000 geöffnet und die Lobbykomponente wird angezeigt.Öffnen Sie eine weitere Registerkarte oder vorzugsweise ein Fenster und kopieren Sie http://localhost:3000 und fügen Sie es ein. Erstellen Sie in einem Fenster einen Kanal, indem Sie auf die Schaltfläche "Erstellen" klicken. Es öffnet sich ein Modal, das die Raum-ID anzeigt. Kopieren Sie diese Kennung und fügen Sie sie ein. Gehen Sie zum anderen Fenster und klicken Sie auf die Schaltfläche "Beitreten". Wenn das Modal erscheint, geben Sie die Raumnummer in das Eingabefeld ein und klicken Sie auf die Schaltfläche "Okay".
Sobald die Spieler verbunden sind, wird das Spiel gestartet. Das Fenster, in dem du den Kanal erstellt hast, macht den ersten Zug. Drücken Sie ein beliebiges Feld auf dem Spielbrett und sehen Sie, wie die Figur X in Echtzeit für beide Fenster auf dem Spielbrett angezeigt wird. Wenn Sie versuchen, ein anderes Feld auf demselben Brett zu drücken, passiert nichts, weil Sie nicht mehr am Zug sind. Drücken Sie im anderen Fenster auf ein beliebiges Feld auf dem Brett und die Figur O wird auf das Feld gesetzt.
Spielen Sie weiter, bis es einen Sieger oder ein Unentschieden gibt. Dann wird ein Modal angezeigt, in dem der Gewinner der Runde bekannt gegeben wird oder das Spiel mit einem Unentschieden endet. In demselben Modal muss Spieler X entscheiden, ob er weiterspielen oder das Spiel beenden möchte. Das Modal für Spieler O wird ihm sagen, dass er auf eine neue Runde warten soll.
Wenn Spieler X das Spiel fortsetzt, wird alles zurückgesetzt, außer dem Spielstand. Andernfalls kehren beide Spieler in die Lobby zurück, wo sie neue Kanäle erstellen oder ihnen beitreten können. Sehen Sie sich die Spieldemo unten an:
Haben Sie Vorschläge oder Fragen zum Inhalt dieses Beitrags? Melden Sie sich unter devrel@pubnub.com.
Inhalt
Konzepte fürEchtzeit-Multiplayer-SpieleApp-ÜbersichtLobbyeinrichtenKanalerstellenKanalbeitretenSpielstartenSpielfunktionenerstellenUmgebunghinzufügenLogikhinzufügenNeue RundestartenSpiel starten
Wie kann PubNub dir helfen?
Dieser Artikel wurde ursprünglich auf PubNub.com veröffentlicht.
Unsere Plattform unterstützt Entwickler bei der Erstellung, Bereitstellung und Verwaltung von Echtzeit-Interaktivität für Webanwendungen, mobile Anwendungen und IoT-Geräte.
Die Grundlage unserer Plattform ist das größte und am besten skalierbare Echtzeit-Edge-Messaging-Netzwerk der Branche. Mit über 15 Points-of-Presence weltweit, die 800 Millionen monatlich aktive Nutzer unterstützen, und einer Zuverlässigkeit von 99,999 % müssen Sie sich keine Sorgen über Ausfälle, Gleichzeitigkeitsgrenzen oder Latenzprobleme aufgrund von Verkehrsspitzen machen.
PubNub erleben
Sehen Sie sich die Live Tour an, um in weniger als 5 Minuten die grundlegenden Konzepte hinter jeder PubNub-gestützten App zu verstehen
Einrichten
Melden Sie sich für einen PubNub-Account an und erhalten Sie sofort kostenlosen Zugang zu den PubNub-Schlüsseln
Beginnen Sie
Mit den PubNub-Dokumenten können Sie sofort loslegen, unabhängig von Ihrem Anwendungsfall oder SDK