Yu Gi Oh em Unity!


Olá pessoal, como vão? Há exatamente um mês eu iniciei o desenvolvimento de um sistema de batalhas ao estilo Yu Gi Oh, mais precisadamente seguindo as regras e design do game para PS1: Yu-Gi-Oh Forbidden Memories.

O sistema foi iniciado com a adição de cartas num banco de dados SQLite, e dentro do Unity a única coisa que ele fazia era instanciar essas cartas do banco para a cena, contendo as informações de cada carta como: pontos de ataque, pontos de defesa, nome da carta, e a imagem dela que era renderizada na Unity através do componente SpriteRenderer.

O vídeo do início da proposta pode ser visualizado logo abaixo:



Porém, com o passar das semanas, o sistema foi ganhando forma e hoje conta com diversas classes para gerenciar cada aspecto do projeto.
No final das contas, o sistema está assim atualmente (27/07/19):



O sistema conta com diversas classes integradas para fazer com que todo o eco-sistema funcione, algumas delas são as seguintes:


>> Banco de dados:
- Sistema de leitura e acesso ao banco de dados para criar os decks de cartas (maiores informações no primeiro vídeo acima);

- Graças à flexibilidade do SQLite, é possível construir de forma simples e direta os decks de cada jogador, basta adicionar à tabela de 'Jogadores' um novo registro de jogador, e ir adicionando ou removendo as cartas registradas no banco de dados para cada jogador, assim é possível criar diversos decks diferentes, com as cartas que desejar!

>> Deck Manager:
Classe responsável por gerenciar tudo o que for associado aos decks dos jogadores:

- Gerenciamento dos decks através de pilhas(stacks);
- Adição de informações das cartas do banco de dados para as pilhas de decks;
- Gerenciamento da quantidade de cartas no deck;
- Embaralhamento do deck ao iniciar a partida através do algoritmo: Fisher-Yates Shuffle.

>> Draw Manager:
Classe responsável por manipular o sistema de Draw (compra) de cartas do jogo.
Essa classe é parcial e foi dividida em duas: 

- Draw Manager Amount:
- Responsável por gerenciar quantas cartas deverão ser instanciadas do deck para a mão dos jogadores, possibilitando instanciar as cinco cartas iniciais, quanto qualquer quantidade subsequente, como por exemplo, ao se utilizar de alguma carta com efeito similar ao Pot Of Greed.
Basta apenas passar como argumento da função de Draw da classe, a quantidade de cartas que deseja instanciar, que o sistema fará automaticamente a compra dessas cartas.

- Também responsável por verificar se o jogador possui cartas suficientes para comprar, e caso não, encerra a jogada fazendo com que o jogador que não tenha mais cartas para comprar, perca.

Por ser uma classe pequena, posso mostrá-la aqui de ante-mão:

using System.Collections; using System.Collections.Generic; using UnityEngine; namespace YuGiOhManager{ public static partial class DrawManager{ private const float timeToAnimation = 0.3F; public static IEnumerator DrawCards(int amount, bool isMainPlayer = true){ var currentDeck = isMainPlayer ? DeckManager.deck : DeckManager.deckAI; for (int i = 0; i < amount; i++) { if (currentDeck.Count > 0) { yield return new WaitForSeconds (timeToAnimation); InstantiateInScene (isMainPlayer); } else { DialogueUI.ShowDialogue ((isMainPlayer ? "Player 1" : "Player 2") + " doesn't have enough card to draw. Lose!"); GameManager.gameOver = true; yield break; } } } } }


- Draw Manager Instance:
Essa classe é responsável por instanciar de fato as cartas na cena, carregando as informações fornecidas do banco de dados.
São muitos processos como: Verificar se a carta a ser instanciada é uma carta do main player ou do bot, passar as informações do banco de dados para os prefabs das cartas, gerenciar o local da mão que essa carta será instanciada, e etc.

>> Hand Manager:
Essa classe fica ao encargo de gerenciar tudo o que diz respeito às cartas na mão do jogador.

- Função que retorna qual "mão" está sendo avaliada (cartas na mão do main player ou do bot);

- Função de Sort Hand (reorganizar as cartas na mão, assim que uma carta é escolhida para ser jogada no campo);

- Função que remove a carta da mão e coloca no tabuleiro, assim que a carta é escolhida.

Essa classe também é muito importante para o sistema de Inteligência Artificial escolher qual carta ela irá jogar de sua mão para o campo.

>> Board Manager:
Nesta classe estão as funções responsáveis por manipular as cartas que estão em campo (no tabuleiro).

- Verificar se o campo está vazio;

- Verificar se o campo está cheio;

- Verificar qual melhor local para colocar a carta em campo;

- Retorna qual campo está sendo analisado (de qual jogador);

- Sistema de Replace Card (substituição). Quando o campo está cheio, deve-se substituir uma carta já existente no tabuleiro por uma selecionada da mão.

>> Battle Manager:
Essa classe tem a responsabilidade de gerenciar tudo o que for relacionado ao sistema de batalha.

- Define as cartas que estão batalhando;

- Define quem está atacando e quem está sendo atacado;

- Verifica quem perdeu ou ganhou a batalha;

- Desconta os pontos de vida do perdedor;

- Verifica possíveis empates;

- Chama a classe de animação de batalha;

- Destrói do campo a carta perdedora da batalha;

- Sistema de atacar pontos de vida diretamente.

>> Battle Animation Manager:
A classe "Battle Manager", como dito acima, é responsável por gerenciar a batalha do jogo, mas a parte do feedback visual dessa batalha fica à cargo da classe: "Battle Animation Manager". Então essa classe é responsável por ativar as animações na cena de acordo com o tipo da batalha.
Quem atacou ganhou da carta atacada? Quem atacou perdeu da carta atacada? Foi empate? Não houve nada? Ataque direto aos pontos de vida? Todas essas animações são gerenciadas através de uma enumeração de ações, de acordo com a batalha que ocorreu e as cartas envolvidas na mesma.
É através dessa classe também que são mostrados os pontos de vidas descontados na tela.

>> Change Turn Manager:
Essa classe faz o papel de gerenciar de quem é a vez, e de encerrar a mesma, quando algum jogador solicitar, chamando os eventos necessários para "reiniciar" a vez do próximo jogador.
Também é responsável por chamar a classe da Inteligência Artificial para que ela possa jogar, caso esteja na vez dela.

>> State Manager:
Aqui não se trata de uma classe em específico, mas sim de um escopo do namespace "YuGiOh System", para armazenar as enumerações que serão utilizadas em todas as classes do sistema, como as enumerações das fases do turno (Draw Phase, Main Phase, Battle Phase, End Phase), o estado atual da carta (OnHand, OnBoard), enumerações dos estados das batalhas (já citados anteriormente), delegados para gerenciar eventos do sistema, e etc.

>> UI Manager:
Essa classe gerencia as informações que serão visualizadas na UI da Unity, como as imagens da carta selecionada, seus pontos e nome.
Seu evento de mostrar essas informações na UI é disparado através de uma outra classe chamada "Mouse Manager" que é um componente do prefab das cartas, que detecta se o cursor do mouse está sobre a carta, assim, caso esteja, as informações daquela carta são mostradas em cada componente da UI, como os de Image e Text.

>> Game Manager:
Essa classe é algo mais generalizada, que visa armazenar informações globais do jogo e não de algo específico como as classes Managers anteriores.

- Armazena os pontos de vida dos jogadores;
- Os turnos dos jogadores;
- Se é o primeiro turno ou não;
- A fase atual;
- Se é game over;
- Se alguma animação de batalha está sendo executada;
- Se a caixa de diálogos está ativada;
- Armazena as cartas atuais que estão sendo selecionadas para alguma ação.

>> Outras classes:
Além dessas classes que gerenciam o sistema, há outras classes mais específicas, como a classe que ativa as caixas de diálogos, o sistema de animações das compras de cartas, as ações de cada carta na mão (Set Up, Set Down e Cancel), as ações das cartas no tabuleiro (Battle, Change Mode, Cancel), inicialização das informações da partida, Mouse Manager, superclasse responsável por receber as informações do banco de dados, e etc.



>> Inteligência Artificial (I.A)





Como o projeto não é online, se fez necessário a inserção de um sistema de I.A básico para jogar contra o jogador principal.
Essa I.A foi muito inspirada no bot dos primeiros oponentes do já citado Yu-Gi-Oh Forbidden Memories do PS1, ou seja, com uma tática ofensiva, tendo a prioridade em atacar seus pontos de vida e destruir as suas cartas, apesar de ela possuir algumas cartas defensivas em seu deck.

A I.A foi dividida em dois scripts: Um que decide qual é a melhor carta para selecionar na mão e jogar no tabuleiro, de acordo com diversos fatores influenciáveis em suas decisões, tais como: A carta que desejo selecionar é capaz de vencer de alguma carta no campo do oponente? Existe alguma carta no MEU campo que já dá conta do recado? É melhor eu me defender? Descarto a carta mais fraca da mão? Ataco os pontos de vida diretamente? e etc.

O outro script é responsável por fazer a I.A decidir qual a sua melhor ação com suas cartas já em campo, como: Vai batalhar? Se sim, contra qual carta do oponente? Vai só se defender? Mudar a posição de batalha de alguma carta? E assim por diante...

No final de vários e vários testes, apesar de uma I.A básica, ela atende bem as expectativas, procurando sempre fazer a melhor jogada que a traga mais benefícios.

>> O que pretendo implementar no futuro?
O sistema está completo com tudo que planejei desde a sua concepção, mas isso não irá me impedir de implementar novos recursos posteriormente.
Alguns que tenho em mente são:

- Adição de cartas do tipo "Spell" e "Trap";

- Deck Viewer, ou seja, um visualizador para as cartas do seu deck;

- Mais inimigos, com decks diferentes, ou seja, vai surgir a necessidade de criar um sistema de seleção de personagens.

>> Onde o sistema vai ser disponibilizado?
Executável do projeto para testar: https://mega.nz/#!lawUGSJa!9UtT2OEqbuTisNxzZMaoJY-DshpNBUXDFm1VVNch9To

Código-fonte no gitHub: https://github.com/mayleone1994/YuGiOh-System/tree/master/YuGiOh_System/Assets/Scripts

Comentários

  1. Curti demais o sistema, o único problema que encontrei testando direto na unity é que quando adiciono um botão para reinicar a cena, não é possível iniciar a batalha novamente, ele buga totalmente.

    ResponderExcluir
    Respostas
    1. Olá, obrigada por testar o sistema!
      Esse problema ocorre devido a algumas variáveis estáticas que precisam ser reiniciadas quando a cena carrega de novo.
      Eu estou trabalhando no sistema ainda e já adicionei um game over definitivo, seleção de personagens(agora sim tem as transições de tela corretamente) e inimigos com outros decks, e um sistema de descartar cartas da mão. Tudo isso que citei já está pronto, em breve irei postar o update!

      Excluir
  2. Obrigado pelo seu feedback :)
    Eu ficarei muito feliz em poder testar esta nova versão, eu consegui reiniciar algumas variáveis, mas estou totalmente perdido, por exemplo quando faço reinicio a tela, não consegue ler as informações do nome atk e def no canvas e o inimigo não consegue jogar a carta dele no campo, sabe quais são as variáveis que devo mexer e em qual script?
    Muito obrigado você é demais parabéns :)

    ResponderExcluir
  3. Olá, gostaria de saber se você está planejando em fazer um tutorial ?

    ResponderExcluir

Postar um comentário