Ejercicio: Lógica del juego
En este ejercicio, agregamos lógica de juego a nuestra aplicación para asegurarnos de que terminamos con un juego totalmente funcional.
Para ayudar a que este tutorial se siga centrando en la formación sobre Blazor, se proporciona una clase denominada GameState
que contiene la lógica para administrar el juego.
Adición del estado del juego
Vamos a agregar la clase GameState
al proyecto y, a continuación, ponerla a disposición de los componentes como un servicio singleton a través de la inserción de dependencias.
Copie el archivo GameState.cs en la raíz del proyecto.
Abra el archivo Program.cs en la raíz del proyecto y agregue esta instrucción para configurar
GameState
como un servicio singleton en la aplicación:builder.Services.AddSingleton<GameState>();
Ahora podemos insertar una instancia de la clase
GameState
en nuestro componenteBoard
.Agregue la siguiente directiva
@inject
en la parte superior del archivo Board.razor para insertar el estado actual del juego en el componente:@inject GameState State
Ahora podemos empezar a conectar nuestro componente
Board
al estado del juego.
Restablecimiento del estado
Comencemos restableciendo el estado del juego cuando el componente Board
se muestra por primera vez en pantalla. Agregaremos código para restablecer el estado del juego cuando se inicializa el componente.
Agregue un método
OnInitialized
con una llamada aResetBoard
, dentro del bloque@code
situado en la parte inferior del archivo Board.razor de la siguiente manera:@code { protected override void OnInitialized() { State.ResetBoard(); } }
Cuando el tablero se muestra por primera vez a un usuario, el estado se restablece al inicio de una partida.
Creación de las piezas del juego
A continuación, asignemos las 42 piezas de juego posibles que se podrían jugar. Podemos representar las piezas del juego como una matriz a la que hacen referencia 42 elementos HTML en el tablero. Podemos mover y colocar esas piezas asignando un conjunto de clases CSS con posiciones de columna y fila.
Defina un campo de matriz de cadenas en el bloque de código para contener nuestras piezas de juego:
private string[] pieces = new string[42];
Agregue código a la sección HTML que crea 42 etiquetas
span
, una para cada pieza del juego, en el mismo componente:@for (var i = 0; i < 42; i++) { <span class="@pieces[i]"></span> }
El código completo debe ser similar al siguiente:
<div> <div class="board">...</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(); } }
Esto asigna una cadena vacía a la clase CSS de cada intervalo de piezas de juego. Una cadena vacía para una clase CSS impide que las piezas del juego aparezcan en la pantalla, ya que no se les aplica ningún estilo.
Control de la colocación de las piezas del juego
Vamos a agregar un método para controlar cuándo un jugador coloca una pieza en una columna. La clase GameState
sabe cómo asignar la fila correcta para la pieza del juego e informa de la fila en la que aterriza. Podemos usar esta información para asignar clases CSS que representan el color del jugador, la ubicación final de la pieza y una animación CSS de colocación.
Llamamos a este método PlayPiece
y acepta un parámetro de entrada que especifica la columna que ha elegido el jugador.
Agregue este código debajo de la matriz
pieces
que hemos definido en el paso anterior.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}"; }
Esto es lo que hace el código PlayPiece
:
- Indicamos al estado del juego que coloque una pieza en la columna enviada denominada
col
y capture la fila en la que ha aterrizado la pieza. - A continuación, podemos definir las tres clases CSS que se deben asignar a la pieza de juego para identificar qué jugador está actuando actualmente, la columna en la que se ha colocado la pieza y la fila de aterrizaje.
- La última línea del método asigna estas clases a esa pieza de juego en la matriz
pieces
.
Si busca en el archivo Board.razor.css proporcionado, encontrará las clases CSS que coinciden con la columna, la fila y el turno del jugador.
El efecto resultante es que la pieza del juego se coloca en la columna y se anima para caer en la fila más baja cuando se llama a este método.
Elección de una columna
A continuación, debemos colocar algunos controles que permitan a los jugadores elegir una columna y llamar a nuestro nuevo método PlayPiece
. Usaremos el carácter "🔽" para indicar que puede colocar una pieza en esta columna.
Encima de la etiqueta inicial
<div>
, agregue una fila de botones en los que se puede hacer clic:<nav> @for (byte i = 0; i < 7; i++) { var col = i; <span title="Click to play a piece" @onclick="() => PlayPiece(col)">🔽</span> } </nav>
El atributo
@onclick
especifica un controlador de eventos para el evento de clic. Pero para controlar los eventos de la interfaz de usuario, es necesario representar un componente de Blazor mediante un modo de representación interactiva. De forma predeterminada, los componentes de Blazor se representan estáticamente desde el servidor. Podemos aplicar un modo de representación interactiva a un componente mediante el atributo@rendermode
.Actualice el componente
Board
de la páginaHome
para usar el modo de representaciónInteractiveServer
.<Board @rendermode="InteractiveServer" />
El modo de representación
InteractiveServer
controlará los eventos de interfaz de usuario de los componentes del servidor a través de una conexión WebSocket con el explorador.Ejecute la aplicación con estos cambios. Debería tener este aspecto ahora:
Mejor, cuando seleccionamos uno de los botones de colocación de la parte superior, se puede observar el siguiente comportamiento:
¡Un gran avance! Ahora podemos agregar piezas al tablero. El objeto GameState
es lo suficientemente inteligente como para dinamizar entre los dos jugadores. Continúe y seleccione más botones de colocación y observe los resultados.
Control de errores y victoria
Si juega con el juego que ha configurado hasta el momento, verá que genera errores al intentar colocar demasiadas piezas en la misma columna y cuando un jugador gana la partida.
Vamos a agregar algunos indicadores y control de errores a nuestro tablero para que el estado actual quede claro. Vamos a agregar un área de estado encima del tablero y debajo de los botones de colocación.
Inserte el marcado siguiente después del elemento
nav
:<nav>...</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>
Este marcado nos permite mostrar indicadores para:
- Anunciar un ganador del juego
- Un botón que nos permite reiniciar el juego
- Mensajes de error
- Turno del jugador actual
Vamos a rellenar alguna lógica para establecer estos valores.
Agregue el código siguiente después de la matriz de piezas:
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;" : "";
- La propiedad
CurrentTurn
se calcula automáticamente en función del estado dewinnerMessage
y de la propiedadPlayerTurn
deGameState
. ResetStyle
se calcula en función del contenido deWinnerMessage
. Si haywinnerMessage
, hacemos que el botón de restablecimiento aparezca en la pantalla.
- La propiedad
Vamos a controlar el mensaje de error cuando se coloca una pieza. Agregue una línea para borrar el mensaje de error y, a continuación, encapsule el código en el método
PlayPiece
con un bloquetry...catch
para establecererrorMessage
si se ha producido una excepción: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; }
Nuestro indicador del controlador de errores es sencillo y usa el marco CSS de arranque para mostrar un error en modo de peligro.
A continuación, vamos a agregar el método
ResetGame
que nuestro botón desencadena para reiniciar un juego. Actualmente, la única manera de reiniciar un juego es actualizar la página. Este código nos permite permanecer en la misma página.void ResetGame() { State.ResetBoard(); winnerMessage = string.Empty; errorMessage = string.Empty; pieces = new string[42]; }
Ahora, nuestro método
ResetGame
tiene la siguiente lógica:- Restablecer el estado del tablero.
- Ocultar nuestros indicadores.
- Restablecer la matriz de piezas en una matriz vacía de 42 cadenas.
Esta actualización debería permitirnos volver a jugar al juego, y ahora vemos un indicador justo encima del tablero que declara el turno del jugador y, en última instancia, la finalización del juego.
Todavía nos encontramos en una situación en la que no podemos seleccionar el botón de restablecimiento. Vamos a agregar lógica en el método
PlayPiece
para detectar el final del juego.Vamos a detectar si hay un ganador de la partida agregando una expresión switch después de nuestro bloque
try...catch
enPlayPiece
.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!", _ => "" };
El método
CheckForWin
devuelve una enumeración que informa del jugador, si alguno ha ganado el juego o si el juego termina en empate. Esta expresión switch establecerá el campowinnerMessage
correctamente si se produce un estado de fin de partida.Ahora, cuando jugamos y llegamos a un escenario de fin de partida, aparecen estos indicadores:
Resumen
Hemos aprendido mucho sobre Blazor y hemos creado un pequeño juego pulido. Estas son algunas de las aptitudes que hemos aprendido:
- Crear un componente
- Agregar el componente a la página principal
- Usar la inserción de dependencias para administrar el estado de un juego
- Hacer que el juego sea interactivo con controladores de eventos para colocar piezas y restablecer el juego
- Escribir un controlador de errores para notificar el estado del juego
- Agregar parámetros a nuestro componente
El proyecto que hemos creado es un juego sencillo, y podría hacer mucho más con él. ¿Busca algunos desafíos para mejorarlo?
Desafíos
Tenga en cuenta los siguientes desafíos:
- Quite el diseño predeterminado y las páginas adicionales de la aplicación para que sea más pequeña.
- Mejore los parámetros del componente
Board
para poder pasar cualquier valor CSS de color válido. - Mejore la apariencia de los indicadores con algunos diseños CSS y HTML.
- Introduzca efectos de sonido.
- Agregue un indicador visual y evite que se use un botón de colocación cuando la columna esté llena.
- Agregue funcionalidades de red para que pueda jugar con un amigo en su navegador.
- Inserte el juego en una aplicación .NET MAUI con Blazor y juegue en su teléfono o tableta.
¡Feliz programación y diviértase!