Exercício – Lógica do jogo
Neste exercício, adicionamos lógica de jogo ao nosso aplicativo para garantir que acabemos com um jogo totalmente funcional.
Para ajudar a manter este tutorial no tópico ensinando sobre Blazor, fornecemos uma classe chamada GameState
que contém a lógica para gerenciar o jogo.
Adicionar o estado do jogo
Vamos adicionar a classe GameState
ao projeto e disponibilizá-la aos componentes como um serviço singleton por meio da injeção de dependência.
Copie o arquivo GameState.cs na raiz do projeto.
Abra o arquivo Program.cs na raiz do projeto e adicione essa instrução para configurar
GameState
como um serviço singleton no seu aplicativo:builder.Services.AddSingleton<GameState>();
Agora podemos injetar uma instância da classe
GameState
em nosso componenteBoard
.Adicione a seguinte diretiva
@inject
no início do arquivo Board.razor. A diretiva injeta o estado atual do jogo no componente:@inject GameState State
Agora podemos começar a conectar nosso componente
Board
ao estado do jogo.
Redefinir estado
Vamos começar redefinindo o estado do jogo quando o componente Board
for pintado na tela pela primeira vez. Adicione um trecho de código para redefinir o estado do jogo quando o componente é inicializado.
Adicione um método
OnInitialized
com uma chamada paraResetBoard
, dentro do bloco@code
na parte inferior do arquivo Board.razor da seguinte maneira:@code { protected override void OnInitialized() { State.ResetBoard(); } }
Quando o tabuleiro é mostrado pela primeira vez a um usuário, o estado é redefinido para o início de um jogo.
Criar peças de jogo
Em seguida, vamos alocar as possíveis 42 peças de jogo que poderiam ser jogadas. Podemos representar as partes do jogo como uma matriz referenciada por 42 elementos HTML no tabuleiro. Podemos mover e colocar essas peças atribuindo um conjunto de classes CSS com posições de coluna e linha.
Para manter nossas peças do jogo, definimos um campo de matriz de cadeia de caracteres no bloco de códigos:
private string[] pieces = new string[42];
Adicione código à seção HTML que cria 42 tags
span
, uma para cada peça do jogo, no mesmo componente:@for (var i = 0; i < 42; i++) { <span class="@pieces[i]"></span> }
Seu código completo deve ter esta aparência:
<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(); } }
Isso atribui uma cadeia de caracteres vazia à classe CSS de cada intervalo de peças do jogo. Uma cadeia de caracteres vazia para uma classe CSS impede que as peças do jogo apareçam na tela, pois nenhum estilo é aplicado a elas.
Manipular o posicionamento de peças do jogo
Vamos adicionar um método para identificar quando um jogador colocar uma peça em uma coluna. A classe GameState
sabe como atribuir a linha correta para a peça do jogo e relata a linha em que ela chega. Podemos usar essas informações para atribuir classes CSS que representam a cor do jogador, a localização final da peça e uma animação de queda CSS.
Chamamos esse método PlayPiece
; ele aceita um parâmetro de entrada de dados que especifica a coluna escolhida pelo jogador.
Adicione este código abaixo da matriz
pieces
que definimos na etapa 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}"; }
Confira o que o código PlayPiece
faz:
- Dizemos ao estado do jogo para reproduzir uma peça na coluna enviada chamada
col
e capturar a linha em que a peça caiu. - Em seguida, podemos definir as três classes CSS a serem atribuídas à peça do jogo para identificar qual jogador está atuando no momento, a coluna em que a peça foi colocada e a linha de aterrissagem.
- A última linha do método atribui essas classes a essa peça de jogo na matriz
pieces
.
Se procurar no Board.razor.css fornecido, você encontrará as classes de CSS que correspondem a coluna, linha e vez do jogador.
O efeito resultante é que a peça do jogo é colocada na coluna e animada para cair na última linha quando esse método é chamado.
Escolher uma cor
Em seguida, precisamos colocar alguns controles que permitem que os jogadores escolham uma coluna e chamem nosso novo método PlayPiece
. Usamos o caractere "🔽" para indicar que você pode soltar uma peça nessa coluna.
Acima da marca inicial
<div>
, adicione uma linha de botões clicáveis:<nav> @for (byte i = 0; i < 7; i++) { var col = i; <span title="Click to play a piece" @onclick="() => PlayPiece(col)">🔽</span> } </nav>
O atributo
@onclick
especifica um manipulador de eventos para o evento de clique. Mas para manipular eventos de interface do usuário, um componente Blazor precisa ser renderizado usando um modo de renderização interativo. Por padrão, os componentes Blazor são renderizados estaticamente do servidor. Podemos aplicar um modo de renderização interativo a um componente usando o atributo@rendermode
.Atualize o componente
Board
na páginaHome
para que ele use o modo de renderizaçãoInteractiveServer
.<Board @rendermode="InteractiveServer" />
O modo de renderização
InteractiveServer
lida com os eventos de interface do usuário de seus componentes a partir do servidor por meio de uma conexão WebSocket com o navegador.Execute o aplicativo com essas alterações. Agora, isso deverá ser parecido com:
Melhor ainda, quando selecionamos um dos botões de soltar na parte superior, o seguinte comportamento pode ser observado:
Esse é um grande progresso! Agora podemos adicionar peças ao tabuleiro. O objeto GameState
é inteligente o suficiente para alternar entre os dois jogadores. Vá em frente e selecione mais botões de soltar e observe os resultados.
Vitória e tratamento de erros
Se jogar o jogo na configuração atual, você vai descobrir que ele gera erros quando você tenta colocar muitas peças na mesma coluna e quando um jogador ganha o jogo.
Vamos deixar claro o estado atual do nosso jogo adicionando alguns indicadores e tratamentos de erros ao nosso tabuleiro. Adicione uma área de status acima do tabuleiro e abaixo dos botões de soltar.
Insira a seguinte marcação após o elemento
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>
Essa marcação nos permite exibir indicadores para:
- Anunciar um vencedor do jogo
- Um botão que nos permite reiniciar o jogo
- Mensagens de erro
- A vez do jogador atual
Agora vamos preencher um trecho de lógica que defina esses valores.
Adicione o seguinte código após a matriz de peças:
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;" : "";
- A propriedade
CurrentTurn
é calculada automaticamente com base no estado dowinnerMessage
e na propriedadePlayerTurn
doGameState
. - O
ResetStyle
é calculado com base no conteúdo doWinnerMessage
. Se houver umwinnerMessage
, faremos com que o botão redefinir apareça na tela.
- A propriedade
Vamos lidar com a mensagem de erro quando uma peça é reproduzida. Adicione uma linha para limpar a mensagem de erro e, em seguida, encapsule o código no método
PlayPiece
com um blocotry...catch
para definir oerrorMessage
, caso tenha ocorrido uma exceção: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; }
Nosso indicador de manipulador de erros é simples e usa a estrutura do CSS de Inicialização para exibir um erro no modo de perigo.
Em seguida, vamos adicionar o método
ResetGame
que nosso botão dispara para reiniciar um jogo. Atualmente, a única maneira de reiniciar um jogo é atualizando a página. Esse código permite permanecer na mesma página.void ResetGame() { State.ResetBoard(); winnerMessage = string.Empty; errorMessage = string.Empty; pieces = new string[42]; }
Agora, nosso método
ResetGame
tem a seguinte lógica:- Redefinir o estado do quadro.
- Ocultar nossos indicadores.
- Redefinir a matriz peças para uma matriz vazia de 42 cadeias de caracteres.
Essa atualização deve nos permitir jogar o jogo novamente, e agora vemos um indicador logo acima do tabuleiro declarando a vez do jogador e, eventualmente, a conclusão do jogo.
Ainda temos uma situação em que não podemos selecionar o botão de redefinição. Vamos adicionar um trecho de lógica ao método
PlayPiece
para detectar o final do jogo.Vamos detectar se há um vencedor no jogo adicionando uma expressão de comutador após nosso bloco
try...catch
emPlayPiece
.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!", _ => "" };
O método
CheckForWin
retorna uma enumeração que relata qual jogador ganhou, se algum tiver ganhado, ou se o resultado do jogo é um empate. Essa expressão de switch definirá o campowinnerMessage
adequadamente se ocorrer um estado de fim de jogo.Agora, quando jogamos e chegamos a um cenário de fim de jogo, esses indicadores aparecem:
Resumo
Aprendemos muito sobre o Blazor e desenvolvemos um joguinho bem bacana. Aqui estão algumas das habilidades que aprendemos:
- Criar um componente
- Adicionar esse componente à nossa home page
- Injeção de dependência usada para gerenciar o estado de um jogo
- Tornar o jogo interativo com manipuladores de eventos para colocar peças e redefinir o jogo
- Escrever um manipulador de erros para relatar o estado do jogo
- Parâmetros adicionados ao nosso componente
O projeto que criamos é um jogo simples, e há muito mais que você poderia fazer com ele. Procurando alguns desafios para melhorá-lo?
Desafios
Considere o seguinte trecho de C#:
- Para diminuir o aplicativo, remova o layout padrão e as páginas adicionais.
- Aprimore os parâmetros do componente
Board
para que você possa passar todo valor de cor CSS válido. - Aprimore a aparência dos indicadores com alguns layouts CSS e HTML.
- Introduza efeitos sonoros.
- Adicione um indicador visual e impeça que um botão de soltar seja usado quando a coluna estiver cheia.
- Adicione recursos de rede para que você possa jogar com um amigo no navegador dele.
- Insira o jogo em um .NET MAUI com aplicativo Blazor e reproduza-o em seu telefone ou tablet.
Feliz programação e divirta-se!