Tutorial do Unity 2D Pac-Man

Tutorial do Unity 2D Pac-Man

Prefácio

Vamos fazer um jogo inspirado no Pac-Man no Unity. O jogo original Pac-Man foi lançado em outubro de 1980 e logo se tornou o mais famoso jogo de arcade de todos os tempos. O jogo ficou tão popular que até mesmo o Unity incluiu uma pequena parte dele em seu mecanismo de jogo:




Neste tutorial, criaremos um clone do Pac-Man com apenas 62 linhas de código usando os poderosos recursos 2D do Unity. Manteremos as coisas o mais simples possível e nos concentraremos no labirinto, nos fantasmas, na comida e, claro, no Pac-Man.
Como de costume, tudo será explicado da maneira mais fácil possível para que todos possam entendê-lo.
Aqui está uma prévia do jogo final em ação:



Requisitos

Conhecimento

Nosso tutorial não requer habilidades especiais. Se você sabe algo sobre a Unity deve ter ouvido sobre GameObjects , Prefabs e Transforms, então você está pronto para ir. E se você não sabe, não se preocupe muito com isso.
Sinta-se à vontade para ler os nossos Tutoriais Unity mais fáceis, como Unity 2D Pong Game, para ter uma ideia do motor primeiro.

Versão Unity

Nosso Tutorial do Pac-Man será desenvolvido com o Unity 5.0.0f4 . Versões mais recentes devem funcionar bem, versões mais antigas podem ou não funcionar. A versão gratuita do Unity 5 agora vem com todos os recursos do mecanismo, o que torna a versão recomendada.

Configuração do projeto

Vamos lá. Vamos começar o Unity e selecionar New Project :





Vamos nomeá-lo pacman , selecione qualquer local como C: \ , selecione 2D e clique em Criar Projeto :



Vamos selecionar a câmera principal na hierarquia e, em seguida, definir a cor de fundo para preto. Também ajustaremos o tamanho e a posição, como mostra a imagem a seguir:


O labirinto

O Sprite do Labirinto

Vamos criar o labirinto típico do Pac-Man. Vamos desenhar um que é inspirado no original, mas não completamente o mesmo: 
Labirinto Pac-Man
Nota: Clique com o botão direito na imagem, selecione Salvar como ... , navegue até a pasta Assets do projeto e salve-a em uma nova pasta Sprites .
Depois de salvá-lo em nosso diretório Project, podemos selecioná-lo na área do projeto :

Em seguida, modifique as Configurações de importação no Inspetor : 


Nota: um valor de pixels por unidade significa que 8 x 8 pixels se encaixarão em uma unidade no mundo do jogo. Nós usaremos esse valor para todas as nossas texturas. Selecionamos o valor 8 porque a distância entre dois Pac-Dots (a comida) é sempre de 8 px e queremos que a distância seja de 1 unidade em nosso jogo. 


Clicando no botão Sprite Editor selecionamos Bottom-Left para o Pivot porque facilita o alinhamento mais tarde.
Agora podemos arrastar o labirinto Sprite da nossa área de projeto para a cena :


Vamos dar uma olhada no Inspetor e posicionar o labirinto em (0, 0)para manter as coisas limpas:

Física do Labirinto

Agora o labirinto é apenas uma imagem, nada mais. Não faz parte do mundo da física, as coisas não colidirão com isso e o Pac-Man poderia atravessar as paredes. Vamos mudar isso adicionando um Collider para cada parede no labirinto.
Vamos selecionar Add Component -> Physics 2D -> Box Collider 2D no Inspector :

Se dermos uma olhada na cena , podemos ver que a Unity envolveu o Collider em todo o labirinto, o que não é exatamente o que queremos:
Maze BoxCollider em Cena

O que realmente queremos é ter um Collider ao redor de cada parede do labirinto. Existem duas maneiras de fazer isso. Poderíamos criar um algoritmo que lê a imagem do labirinto e gera Colliders com base nele, ou podemos simplificar e adicionar todos os Colliders manualmente.
Vamos clicar no botão Edit Collider no Inspector : 

Nota: este botão é novo no Unity 4.6, então certifique-se de usar a versão mais recente se você não vir um botão lá.
Isso nos permite modificar o Collider na cena usando os pontos verdes:
Editar colisor em cena

Vamos repetir este processo para todas as paredes do nosso labirinto. Tudo o que precisamos fazer é selecionar Incluir Componente -> Física 2D -> Box Collider 2D , pressionar o botão Editar Colisor e depois modificá-lo na Cena até que ele caiba na próxima parede.
É muito importante que cada Collider seja perfeitamente exato. Por exemplo, se aumentarmos o zoom, a linha verde ainda deverá estar perfeitamente envolvida na caixa azul:
Maze Collider ampliou
O truque é escolher as propriedades OffSet e Size do Collider para que elas sejam sempre como 1,25 ou 1,5 ou 1,75 ou 2,00 , e nunca como 1,24687 ou 1,25788 . Aqui estão alguns exemplos: 



Nota: se mais tarde o seu Pac-Man ficar preso no labirinto ou tiver problemas em se mover, é porque os Maze Colliders não são perfeitamente exatos.
Outra forma de melhorar o movimento impedindo que o Pac-Man trave é definir um Edge Radius como 0.01.
Aqui está o resultado final: 
Labirinto com todos os Colliders em Cena
Nota: ajustar todos os Colliders pode levar alguns minutos.

Adicionando Pac-Man

O Sprite Pac-Man

Agora é hora da parte mais importante do nosso jogo: Pac-Man. Vamos precisar de uma animação para cada direção de movimento: 
direita
esquerda
cima
baixo
Vamos desenhar todas as animações em uma imagem, onde há uma animação por linha: 
Animações Unity Pac-Man
Nota: clique com o botão direito na imagem, selecione Salvar como ... e salve-a na pasta Assets     / Sprites do projeto .
Usaremos as seguintes configurações de importação para ele:



Cortando o Sprite Pac-Man

É importante definir o Sprite Mode para Multiple , que diz ao Unity que há mais de um Pac-Man em nosso Sprite. Vamos abrir o Sprite Editor clicando no botão:


Agora podemos dizer ao Unity onde cada fatia de animação do Pac-Man está localizada em nosso Sprite. Nós selecionaremos Slice e depois fatiaremos como 16 x 16 Grid e depois pressionaremos o botão Slice:


Uma vez que o Sprite foi cortado, podemos fechar o Sprite Editor novamente. Se a Unity nos perguntar sobre Configurações de importação não aplicadas , clicaremos em Aplicar .
Como resultado, agora temos 12 fatias abaixo do nosso Sprite Pac-Man na área do projeto:

Criando as animações do Pac-Man

Ok, agora que temos as fatias de animação, podemos criar nossas 4 animações a partir dele. Como lembrete, aqui estão as animações que vamos precisar:
  • rigth(fatia 0, 1 e 2) (direita)
  • left(fatia 3, 4 e 5) (esquerda )
  • up (fatia 6, 7 e 8) ( para cima )
  • down (fatia 9, 10 e 11) (para baixo)
Vamos criar a animaçãoComeçaremos selecionando as três primeiras fatias na área do projeto:


E, em seguida, arrastando-os para a cena:


Agora, sempre que arrastarmos várias fatias para a Scene, o Unity saberá que queremos criar uma animação a partir delas, por isso nos pergunta automaticamente onde salvar a animação. Vamos salvá-lo como right.anim em uma nova pasta PacmanAnimation . A Unity acabou de adicionar um GameObject pacman_0 à cena e dois arquivos à área do projeto:


O primeiro arquivo é a máquina de estado de animação que especifica coisas como a velocidade da animação e as combinações de transição de imagens. O segundo é a animação em si.
Vamos repetir este processo para o resto das animações (Slice 3, 4, 5 para a esquerda; Slice 6, 7, 8 para cima e Slice 9, 10, 11 para baixo) .
Aqui está o que nossa Hierarquia parece depois:


Limpar a Unity

A Unity criou um GameObject para cada animação, mas só precisamos do primeiro como veremos em breve. Vamos selecionar os outros 3 e depois clicar com o botão direito e deletá- los:

Uma coisa semelhante aconteceu em nossa área de projeto . Agora temos 4 animações e 4 máquinas de estado de animação:

Animação Pac-Man na área do projeto
Novamente, precisamos apenas de uma máquina de estado de animação, então vamos deletar as outras três:
Excluir animação do Pac-Man na área do projeto

A Máquina de Estado de Animação Pac-Man

No momento, temos quatro arquivos de animação, mas o Unity não sabe quando reproduzir qual animação ainda. A solução para o nosso problema faz parte do inacreditável e poderoso sistema de animação Mecanim da Unity Vamos precisar de uma máquina de estado de animação que tenha 4 estados:
  • right
  • left
  • up
  • down
Também vamos adicionar Transitions para que o Unity saiba quando mudar de um estado de animação para outro.
Nota: Unity irá reproduzir a animação right repetidamente enquanto estiver no estado rightEle usará Transições para saber quando mudar para outro estado. O Unity faz tudo isso automaticamente, tudo o que temos a fazer é notificá-lo sobre a direção do movimento do Pac-Man dentro de um Script mais tarde.
Ok, então vamos clicar duas vezes no arquivo de máquina de estado de animação pacman_0 em nossa área de projeto:


Agora podemos ver a máquina de estado no Animator:


Pac-Man Animator Default

Criando os outros estados

 estado right já está no Animator, então vamos adicionar o estado left simplesmente arrastando o left.anim arquivo do PacmanAnimation pasta da nossa Área de Projecto no Animator:


Animador Pac-Man com estados de esquerda
Esse processo pode ser repetido nos dois estados restantes:


Pac-Man Animator todos os estados
O grande negócio sobre a Mecanim é que ela cuidará dos estados de animação por conta própria, de forma totalmente automática. Tudo o que temos a fazer é dizer a Mecanim para tomar cuidado com a direção do movimento do nosso Pac-Man, nada mais. A Mecanim irá então alternar entre estados de animação por si só, sem que precisemos fazer nada.

Criando os Parâmetros

Então, vamos dizer ao Mecanim que ele deve tomar cuidado com a direção do movimento do Pac Man. Uma direção de movimento é do tipo Vector2, a seguinte imagem mostra algumas possíveis direções de movimento:
Vector2 Direções
Nosso Pac-Man terá apenas 4 direções de movimento (cima, baixo, esquerda, direita) . O que significa que precisamos apenas das 4 transiçõesa seguir em nossa máquina de estado de animação:
  • Se DirY> 0, então vá para up (como Diry = 1, Diry = 2, Diry = 3 e assim por diante)
  • Se DirY <0, em seguida, vai para down(como Diry = -1, Diry = -2, Diry = -3 e assim por diante)
  • Se DirX> 0, em seguida, vá para a right(como DirX = 1, DirX = 2, DirX = 3 e assim por diante)
  • Se DirX <0, em seguida, vá para a left (como DirX = -1, DirX = -2, DirX = -3 e assim por diante)
Vamos dar uma olhada na área esquerda do Animator e selecionar a aba Parameters:


Guia Parâmetros do Animador
Aqui vamos clicar no + à direita e depois adicionar um parâmetro do tipo Float com o nome DirX e outro parâmetro do tipo Float com o nome DirY:


Parâmetros do animador Pac-Man
Mais tarde, podemos definir esses parâmetros a partir de um script, escrevendo:
GetComponent<Animator>().SetFloat("DirX"0);
GetComponent<Animator>().SetFloat("DirY"0);

Criando as Transições

Queremos que o Unity mude automaticamente para diferentes estados de animação com base nesses parâmetros. Transições são usadas para alcançar exatamente isso. Por exemplo, podemos adicionar uma Transição da right para a left com a Condição de que DirX> 0 . No entanto, é considerado uma boa prática ter uma pequena tolerância a erros porque a comparação de ponto flutuante nem sempre é perfeita, por isso usaremos o DirX> 0.1 .
Agora também teríamos que usar um DirX> 0.1 Transition de todos os outros estados para a rightPara nos salvar de fazer todo esse trabalho, podemos usar o Any State do Unity .
Any State significa, literalmente, qualquer estado. Então, se criarmos uma transição de qualquer Estado para  right , então é o mesmo que criar uma transição da left , up down para a right.
Vamos clique com o botão direito em Any State e selecione Make Transition . Depois, podemos arrastar a seta branca para o estado right:


Pac-Man Animator Transition de qualquer estado para direita
Agora podemos clicar na seta branca e dar uma olhada no Inspetor,onde podemos modificar a Condição da Transição (ou, em outras palavras, quando deve mudar) . Vamos configurá-lo para DirX> 0.1:


Nota: pressione o + no canto inferior direito para adicionar uma condição.
Vamos também desabilitar o Can Transition To Self nas configurações


Pacman Animator Transition Anystate to Right sem Transição para Si
Nota: isso evita situações estranhas em que uma animação seria reiniciada o tempo todo enquanto pressionava uma tecla de movimento.
Como resultado, sempre que o Pac-Man caminha para a direita (DirX> 0.1) , o animador mudará para o estado correto e reproduzirá a animação.
Nós vamos adicionar mais 3 Transições:
  • Qualquer Estado para a esquerda com a Condição DirX <-0.1
  • Qualquer estado para cima com a condição DirY> 0.1
  • Qualquer Estado para baixo com a condição DirY <-0.1
Aqui está como parece no Animator agora:


Animador Pac-Man com todas as transições
Estamos quase terminando com nossa máquina de estado de animação. Há um último ajuste a ser feito aqui, então vamos selecionar todos os estados e então modificar sua Velocidade no Inspetor para que a animação não pareça muito rápida:


Velocidades do Pac-Man Animator
Se pressionarmos Play , já poderemos ver a animação certa:


Animação direita Pac-Man

Nomenando e posicionado o  Pac-Man

Queremos manter tudo limpo e bom, então vamos selecionar o GameObject pacman_0 na Hierarquia e depois renomeá-lo para pacman (clique com o botão direito e selecione renomear, ou pressione F2):

Pac-Man renomeado


Nós também mudaremos sua posição para (14, 14) para que não saia mais do labirinto:

Física Pac-Man

Neste momento o Pac-Man é apenas uma imagem, nada mais. Ele não faz parte do mundo da física, as coisas não vão colidir com ele e ele não pode se mover ou qualquer outra coisa. Nós precisaremos adicionar um Collider para torná-lo parte do mundo da física, o que significa que as coisas vão colidir com o Pac-Man ao invés de atravessá-lo.
O Pac-Man também deve se movimentar. Um Rigidbody cuida de coisas como gravidade, velocidade e outras forças que fazem as coisas se moverem. Como regra geral, tudo no mundo da física que deve se movimentar precisa de um corpo rígido .

Vamos selecionar o pacman GameObject na Hierarquia , selecionar Add Component -> Physics 2D -> Circle Collider 2D assim como Add Component -> Physics 2D -> Rigidbody 2D . É importante usar exatamente as mesmas configurações mostradas na imagem a seguir:


Tudo bem, o Pac-Man agora faz parte do mundo da física. Ele colidirá com outras coisas e outras coisas colidirão com ele. Isso também fará com que a função OnCollisionEnter2D seja chamada em qualquer script anexado ao Pac-Man.

The Movement Script

Tudo bem, agora existem várias maneiras de fazer o Pac-Man se mover. A maneira mais fácil seria criar um Script que simplesmente verifica as teclas de seta e depois mover o Pac-Man um pouco para cima / baixo / esquerda / direita quando necessário. Isso poderia funcionar, mas não parece muito bom.
Em vez disso, tentaremos ser mais exatos. Sempre que o jogador pressiona uma das teclas de seta, o Pac-Man deve mover exatamente 1 unidade na direção desejada. Os Pac-Dots (a comida) também serão posicionados com uma distância de 1 unidade entre eles, então só faz sentido que o Pac-Man se mova exatamente uma unidade.

Vamos selecionar pacman na Hierarquia e pressionar Add Component-> New Script , nomeie PacmanMove e selecione CSharp como idioma. Também moveremos o Script para uma nova pasta Scripts na Área do Projeto (apenas para manter as coisas limpas) :
Script de Movimento da Unidade na Área do Projeto

Tudo bem, vamos clicar duas vezes no script para que ele seja aberto:
// Comment
using UnityEngine;
using System.Collections;
public class PacmanMove : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }

função Start é chamada automaticamente pelo Unity ao iniciar o jogo. função de atualização é chamada automaticamente repetidamente, aproximadamente 60 vezes por segundo (isso depende da taxa de quadros atual, pode ser menor se houver muitas coisas na tela) .
Há ainda outro tipo de função Update , que é FixedUpdate . Também é chamado repetidamente, mas em um intervalo de tempo fixo. A Física da unidade é calculada no mesmo intervalo de tempo, por isso é sempre uma boa ideia usar FixedUpdate ao fazer coisas de física:




// Comment
using UnityEngine;
using System.Collections;

public class PacmanMove : MonoBehaviour {
    // Use this for initialization    
    void Start () {
    }
    // Update is called once per frame    
    void FixedUpdate () {
    }
}

Vamos precisar de uma variável pública para que possamos modificar a velocidade de movimento no Inspetor mais adiante:
// Comment
using UnityEngine;
using System.Collections;

public class PacmanMove : MonoBehaviour {
    public float speed = 0.4f;
    // Use this for initialization    
    void Start () {
    }
    // Update is called once per frame    
    void FixedUpdate () {
    }
}
Também precisaremos de uma maneira de descobrir se o Pac-Man pode se mover em uma determinada direção ou se há uma parede. Então, por exemplo, se quisermos descobrir se há uma parede no topo do Pac-Man, poderíamos simplesmente lançar uma linha de uma unidade acima do Pac-Man para o Pac-Man e ver se ele acertou em alguma coisa. Se atingisse o próprio Pac-Man, então não havia nada entre os dois, caso contrário deveria haver uma parede.
Aqui está a função:

// Comment
using UnityEngine;
using System.Collections;

public class PacmanMove : MonoBehaviour {
    public float speed = 0.4f;
    // Use this for initialization    
    void Start () {
    }
    // Update is called once per frame    
    void FixedUpdate () {
    }

    bool valid(Vector2 dir){
  Vector2 pos = transform.position;
  RaycastHit2D hit = Physics2D.Linecast(pos + dir,pos);
  return (hit.collider == GetComponent<Collider2D>());
 }
}
Nota: nós simplesmente lançamos a Linha do ponto ao lado de Pac-Man ( pos + dir ) para o próprio Pac-Man ( pos ).
Também precisaremos de uma função que faça o Pac-Man mover uma unidade na direção desejada. Claro que poderíamos apenas fazer:

 
  transform.position += dir;
 
Mas isso pareceria muito abrupto porque é um passo tão grande. Em vez disso, queremos que ele vá lá suavemente, um pequeno passo no momento. Vamos armazenar o destino do movimento em uma variável e, em seguida, usar FixedUpdate para ir em direção a esse destino, passo a passo:
// Comment
using System.Collections;
//using System.Collections.Generic;
using UnityEngine;

public class PacManMove : MonoBehaviour {

 public float speed = 0.4f;
 Vector2 dest = Vector2.zero;
 

 // Use this for initialization
 void Start () {
  dest = transform.position;
 }
 
 // Update is called once per frame
 void FixedUpdate () {
  Vector2 p = Vector2.MoveTowards(transform.position,dest,speed);
  GetComponent<Rigidbody2D>().MovePosition(p);   
   if(Input.GetKey("up")){
    dest = (Vector2)transform.position + Vector2.up;
   }
   if(Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up)){
    dest = (Vector2)transform.position - Vector2.up;
   }
   if(Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right)){
    dest = (Vector2)transform.position + Vector2.right;
   }
   if(Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right)){
    dest = (Vector2)transform.position - Vector2.right;
   }
  
 }

 bool valid(Vector2 dir){
  Vector2 pos = transform.position;
  RaycastHit2D hit = Physics2D.Linecast(pos + dir,pos);
  return (hit.collider == GetComponent<Collider2D>());
 }
}



Nota: usamos o GetComponent para acessar o componente Rigidbody do Pac-Man. Em seguida, usamos para fazer o movimento (nunca devemos usar transform.position para mover GameObjects que possuem Rididbodies).
Vamos também tomar cuidado com as teclas de seta sempre que não estivermos nos movendo. 
Nota: não estamos nos movendo se a posição atual for igual ao destino.
Aqui está nossa função FixedUpdate com verificações de entrada:

// Comment
using System.Collections;
//using System.Collections.Generic;
using UnityEngine;

public class PacManMove : MonoBehaviour {

 public float speed = 0.4f;
 Vector2 dest = Vector2.zero;
 

 // Use this for initialization
 void Start () {
  dest = transform.position;
 }
 
 // Update is called once per frame
 void FixedUpdate () {
  Vector2 p = Vector2.MoveTowards(transform.position,dest,speed);
  GetComponent <Rigidbody2D>().MovePosition(p);   
   if(Input.GetKey("up")){
    dest = (Vector2)transform.position + Vector2.up;
   }
   if(Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up)){
    dest = (Vector2)transform.position - Vector2.up;
   }
   if(Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right)){
    dest = (Vector2)transform.position + Vector2.right;
   }
   if(Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right)){
    dest = (Vector2)transform.position - Vector2.right;
   }
 }

 bool valid(Vector2 dir){
  Vector2 pos = transform.position;
  RaycastHit2D hit = Physics2D.Linecast(pos + dir,pos);
  return (hit.collider == GetComponent<Collider2D>());
 }
}

Nota: transform.position é convertido em Vector2 porque esta é a única maneira de comparar ou adicionar outro Vector2. Também -Vector2.right significa esquerdo e -Vector2.up significa baixo .

Se salvarmos o Script e pressionar Play , poderemos agora mover o Pac-Man com as setas:


Movimento Pac-Man com Setas

Definindo os parâmetros de animação

Agora podemos perfeitamente mover o Pac-Man pelo labirinto, mas o animador não reproduz todas as animações ainda. Não tem problema, vamos apenas modificar nosso código para calcular a direção do movimento atual e depois definir os parâmetros do animador:

// Comment
using System.Collections;
//using System.Collections.Generic;
using UnityEngine;

public class PacManMove : MonoBehaviour {

 public float speed = 0.4f;
 Vector2 dest = Vector2.zero;
 

 // Use this for initialization
 void Start () {
  dest = transform.position;
 }
 
 // Update is called once per frame
 void FixedUpdate () {
  Vector2 p = Vector2.MoveTowards(transform.position,dest,speed);
  GetComponent<Rigidbody2D>().MovePosition(p);   
   if(Input.GetKey("up")){
    dest = (Vector2)transform.position + Vector2.up;
   }
   if(Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up)){
    dest = (Vector2)transform.position - Vector2.up;
   }
   if(Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right)){
    dest = (Vector2)transform.position + Vector2.right;
   }
   if(Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right)){
    dest = (Vector2)transform.position - Vector2.right;
   }
   Vector2 dir = dest - (Vector2)transform.position;    
   GetComponent <Animator>().SetFloat("DirX", dir.x);    
   GetComponent<Animator>().SetFloat("DirY", dir.y);
 }

 bool valid(Vector2 dir){
  Vector2 pos = transform.position;
  RaycastHit2D hit = Physics2D.Linecast(pos + dir,pos);
  return (hit.collider == GetComponent<Collider2D>());
 }
}
Nota: calculamos a direção do movimento atual com a matemática básica do vetor. Tudo o que tínhamos a fazer era subtrair o atual posição do destinação.

E isso é tudo que existe para isso. Se salvarmos o Script e tocarmos Play, poderemos ver as animações adequadas:


Movimento Pac-Man animado

Desenho de Pac-Man em primeiro plano

Antes de começarmos a trabalhar nos Pac-Dots, devemos nos certificar de que o Pac-Man esteja sempre na frente deles. Estamos fazendo um jogo 2D, então não há realmente nenhuma ordem Z como em jogos 3D. Isso significa que o Unity apenas desenha objetos da maneira que lhe agrada. Vamos nos certificar de que a Unidade sempre atraia o Pac-Man na frente de todo o resto.
Existem duas maneiras de fazer isso. Poderíamos quer mudar a propriedade  Renderer Sprite 's Camada Sorting ou podemos mudar a propriedade Order in Layer . Renderer Sprite 's Camada Sorting é importante para jogos maiores com muito mais objetos. Para nós, basta simplesmente alterar o Order in Layer  para 1



Nota: Unity desenha objetos classificados por ordem. Começa com a ordem mais baixa e continua com pedidos mais altos. Então, se o Pac-Man tiver a ordem 1, ele sempre será sorteado depois do labirinto, da comida e de qualquer outra coisa que tenha a ordem 0. E, como ele é atraído depois de todo o resto, ele está automaticamente à frente de todo o resto.

Os Pac-Dots

Os pequenos pontos que o Pac-Man pode comer são chamados Pac-Dots. Alguns agora os chamam  de 'frutos' ou apenas 'comida'. Começaremos desenhando uma pequena imagem de 2x2 px de um Pac-Dot:
Pacdot

Nota: clique com o botão direito na imagem, selecione Salvar como ... e salve-a na pasta Ativos / Sprites do projeto .
Usaremos as seguintes configurações de importação para ele:


Depois podemos arrastá-lo para a cena . Queremos ser avisados ​​quando o Pac-Man passar por um Pac-Dot. Tudo o que precisamos fazer é selecionar Incluir Componente -> Física 2D -> Box Collider 2D no Inspetor e selecionar Is Trigger


Nota: um Colisor com o IsTrigger ativado recebe apenas informações de colisão, ele não colide fisicamente com outras coisas.
A unidade chamará automaticamente a função OnTriggerEnter2D sempre que o Pac-Man ou um dos Ghosts passarem pelo Pac-Dot.
Então, vamos selecionar Adicionar Componente -> Novo Script , nomeie-o como Pacdot , selecione CSharp , mova-o para a pasta Scripts e abra-o:


// Comment
using UnityEngine;
using System.Collections;
public class Pacdot : MonoBehaviour {
    // Use this for initialization    
void Start () {
    }
    // Update is called once per frame    
void Update () {
    }
}

Não precisaremos da função Iniciar ou Atualizar , então vamos remover os dois. Em vez disso, usaremos a função OnTriggerEnter2D mencionada anteriormente :
// Comment
using UnityEngine;
using System.Collections;
public class Pacdot : MonoBehaviour {
    void OnTriggerEnter2D(Collider2D co) {        
// Codigos...    }
}
// Comment
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Pacdot : MonoBehaviour {

 void OnTriggerEnter2D(Collider2D co) {
        if (co.name == "PacMan")
            Destroy(gameObject);
    }
}

Nota: se quiséssemos implementar um recorde no nosso jogo, então este seria o lugar para criá-lo.
Agora podemos clicar com o botão direito no Pac-Dot na Hierarquia , selecionar Duplicar e movê-lo para a próxima posição livre. É importante que sempre posicionemos os Pac-Dots em coordenadas arredondadas como (1, 2) e nunca (1,003, 2,05) .

Aqui está o resultado depois de duplicar e posicionar o Pac-Dot repetidamente:


Todos os Pac-Pontos posicionados
Se pressionarmos Play , o Pac-Man agora pode comê-los:


Pac-Dots sendo comido
Sinta-se à vontade para também mover todos os Pac-Dots para o GameObject do labirinto:


Pac-Dots no Labirinto GameObject
Para que possamos recolher o GameObject do labirinto para ter uma visão limpa na hierarquia:


Pontos de Pac recolhidos no labirinto

Os ghosts

Um bom clone do Pac-Man precisa de alguns inimigos, então vamos adicionar alguns fantasmas.

A imagem do fantasma vermelho

Como de costume, começaremos desenhando um Sprite fantasma com todas as animações nele contidas. Cada linha contém uma animação:
  • certo
  • esquerda
  • acima
  • baixa
O primeiro deles será o fantasma vermelho, também conhecido como Blinky


Pac-Man Blinky
Nota: clique com o botão direito na imagem, selecione Salvar como ... e salve-o na pasta Ativos / Sprites do projeto .
Vamos selecioná-lo na área do projeto e modificar as configurações de importação no Inspetor:


Criando as animações

É importante que definamos o Sprite Mode para Multiple novamente porque o nosso Sprite contém várias fatias. Vamos clicar no botão Sprite Editor e dividir como uma grade de 16 x 16:


Depois, podemos fechar o Sprite Editor e pressionar Aplicar . Agora é hora de criar as animações arrastando as fatias para a cena, assim como fizemos com o Pac-Man.
No início, arrastaremos Slice 0 e 1 para a Scene e salvaremos a animação como right.anim em uma nova pasta BlinkyAnimation .
Vamos repetir este processo para:
  • Fatia 2 e 3 à left
  • Fatia 4 e 5 como up
  • Fatia 6 e 7 como down

Limpar após o Unity

Agora podemos limpar a Hierarquia novamente excluindo os GameObjects blinky_2 , blinky_4 e blinky_6:


Apagar Blinky na Hierarquia
Também podemos excluir os arquivos blinky_2 , blinky_4 e blinky_6 da pasta BlinkyAnimation na área do projeto:


Excluir Blinky na área do projeto

A máquina de estados de animação

Vamos dar um duplo clique no arquivo blinky_0 para abrir o Animator:


Blinky padrão Animator
Vamos criar exatamente a mesma máquina de estado de animação que usamos para o Pac-Man antes. Tudo o que temos a fazer é (1) arrastar as animações, (2) criar os parâmetros e depois  (3) definir as transições:
  • Qualquer Estado para rigth (direita) com a Condição DirX> 0,1
  • Qualquer Estado para a left (esquerda) com a Condição DirX <-0.1
  • Qualquer estado para up (cima) com a condição DirY> 0.1
  • Qualquer Estado para down (baixo) com a condição DirY <-0.1
Aqui está a última máquina de estado de animação no Animator:


Blinky final Animator

Renomeando e posicionando Blinky

Vamos nos certificar de manter tudo limpo e bom. Vamos selecionar o GameObject blinky_0 na Hierarquia e renomeá-lo para blinky:


Blinky renomeado
Nós também mudaremos a posição no Inspetor para que Blinky esteja no meio do labirinto:


Posição Blinky

Física Fantasma

Tudo bem, então Blinky deveria fazer parte do mundo da física novamente. Vamos selecionar Add Component -> Physics 2D -> Circle Collider 2D no Inspector e atribuir as seguintes propriedades: 


Colidir Blinky

Nota: habilitamos Is Trigger porque Blinky é um fantasma, e os fantasmas podem percorrer as coisas.
Blinky também deve se mover pelo labirinto, o que significa que precisaremos adicionar um Rigidbody a ele. Vamos selecionar Add Component -> Physics 2D -> Rigidbody 2D:


Blinky Rigidbody

Trazendo Blinky para o Primeiro Plano

Assim como Pac-Man, queremos que Blinky seja desenhado em primeiro plano, na frente dos Pac-Dots. Tudo o que precisamos fazer é definir o valor Order in Layer como 1:


Blinky Order in Layer

O movimento fantasma

Tudo bem, então não sabemos muito sobre como o AI funciona no jogo Pac-Man original, exceto pelo fato de ser determinístico (que é uma palavra chique para não aleatório) . Também parece que alguns fantasmas estão mais focados em se movimentar pelo labirinto, enquanto outros estão mais focados em seguir o Pac-Man, ou talvez até mesmo tentar se posicionar na frente dele.
Neste Tutorial vamos nos concentrar nas opções mais fáceis de IA: movimentar-se pelo labirinto. Vamos criar um script de movimento de waypoint que faça Blinky percorrer um determinado caminho. O que parece quase simples demais é na verdade uma solução bastante decente para o nosso problema de IA. Quanto mais longo e complexo for o caminho, mais difícil será para o jogador escapar de Blinky.
Vamos selecionar Incluir Componente -> Novo Script , nomeá-lo como GhostMove , selecionar CSharp e movê-lo para a pasta Scripts . Depois disso, podemos clicar duas vezes para abrir:
// Comment
using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

Nota: Não precisaremos da função Start ou Update , em vez disso, usaremos a função FixedUpdate (porque é para coisas físicas como movimento) :
// Comment
using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void FixedUpdate () {

    }
}
Vamos adicionar um array Transform público para que possamos definir os waypoints no Inspector mais tarde:
// Comment
using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    public Transform[] waypoints;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void FixedUpdate () {

    }
}
Nota: uma matriz significa que é mais do que apenas uma transformação.
Também precisaremos de algum tipo de variável de índice que rastreie o waypoint no qual o Blinky está caminhando:
// Comment
using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    public Transform[] waypoints;
    int cur = 0;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void FixedUpdate () {

    }
}
Nota: o waypoint atual pode sempre ser acessado com waypoints [cur] .
// Comment
using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    public Transform[] waypoints;
    int cur = 0;
    public float speed = 0.3f;
    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void FixedUpdate () {

    }
}
Agora podemos usar a função FixedUpdate para nos aproximarmos do waypoint atual, ou selecionar o próximo assim que chegarmos nele:
// Comment
using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    public Transform[] waypoints;
    int cur = 0;
    public float speed = 0.3f;
    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void FixedUpdate () {
        // Waypoint not reached yet? then move closer
        if (transform.position != waypoints[cur].position) {
            Vector2 p = Vector2.MoveTowards(transform.position,
                                        waypoints[cur].position,
                                        speed);
            GetComponent<Rigidbody2D>().MovePosition(p);
        }
        // Waypoint reached, select next one
        else cur = (cur + 1) % waypoints.Length;

    }
}
Nota: usamos a função Vector2.MoveTowards para calcular um ponto um pouco mais próximo do waypoint. Depois, definimos a posição do fantasma com rigidbody2D.MovePosition . Se o waypoint for alcançado, aumentamos a variável cur em um. Também queremos redefinir o cur para 0 se exceder o comprimento da lista. Poderíamos usar algo como if (cur == waypoints.Length) cur = 0 , mas usar o operador modulo ( % ) faz isso parecer um pouco mais elegante.
// Comment
using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    public Transform[] waypoints;
    int cur = 0;
    public float speed = 0.3f;
    
    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void FixedUpdate () {
        // Waypoint not reached yet? then move closer
        if (transform.position != waypoints[cur].position) {
            Vector2 p = Vector2.MoveTowards(transform.position,
                                        waypoints[cur].position,
                                        speed);
            GetComponent<Rigidbody2D>().MovePosition(p);
        }
        // Waypoint reached, select next one
        else cur = (cur + 1) % waypoints.Length;
        Vector2 dir = waypoints[cur].position - transform.position;
        GetComponent<Animator>().SetFloat("DirX", dir.x);
        GetComponent<Animator>().SetFloat("DirY", dir.y);

    }
}
Otimo, há uma última coisa a acrescentar ao nosso roteiro. Os fantasmas devem destruir o Pac-Man ao colidir com ele:
// Comment
 void OnTriggerEnter2D(Collider2D co) {
    if (co.name == "PacMan")
        Destroy(co.gameObject);

}
Nota: sinta-se à vontade para diminuir a vida do Pac-Man ou mostrar uma tela de Game Over neste momento.

Adicionando Waypoints

Tudo bem, vamos adicionar alguns waypoints para o Blinky. Começaremos selecionando GameObject -> Create Empty no menu superior. Vamos mudar o nome para Blinky_Waypoint0 e então atribuir um Gizmo para que possamos vê-lo mais facilmente no Scene:


Blinky Waypoint 0 no Inspetor
Nota: um Gizmo é apenas um ajudante visual, não o vemos ao jogar o jogo.
Vamos também posicioná-lo em (15, 20) . Aqui está como parece na cena agora:


Blinky Waypoint 0 em Cena
Agora podemos duplicar o Waypoint, renomeá-lo para Blinky_Waypoint1 e posicioná-lo em (10, 20):

Blinky Waypoint 1 em Cena
Então Blinky_Waypoint2 em (10, 14) :


Blinky Waypoint 2 em Cena
Blinky_Waypoint3 em (19, 14) :


Blinky Waypoint 3 em Cena
E finalmente Blinky_Waypoint4 em (19, 20) :


Blinky Waypoint 4 em Cena
E como o nosso Script de Movimento continuará automaticamente caminhando até o primeiro waypoint depois que o último foi atingido, teremos um loop perfeito.
Vamos selecionar a Blinky novamente na hierarquia e, em seguida, arrastar um waypoint após o outro para o slot Waypoints do nosso script GhostMove:


Script Blinky GhostMove com Waypoints
Se pressionarmos Play , poderemos ver como Blinky se move ao longo dos waypoints:


Mover Blinky
Naturalmente, os waypoints atuais são bastante simples. Então, fique à vontade para criar uma rota mais complexa como esta:


Waypoints finais de Blinky

Pinky, Inky e Clyde

Nós não queremos que Blinky se sinta solitário lá, então vamos repetir o mesmo fluxo de trabalho para Pinky , Inky e Clyde  


Todos os fantasmas
Nota: é importante usar diferentes waypoints e velocidades de movimento para cada fantasma para tornar o jogo mais desafiador.
Se apertarmos o Play , podemos jogar uma partida do Pac-Man:


Unity 2D Pac-Man

Resumo

Acabamos de criar um clone de Pac-Man 2D sólido, rápido e simples no Unity. Aprendemos sobre Pixels para Units, Mecanim, Colliders, Rididbodies, Layer Orders e Scripting. E mesmo que a IA seja muito simples e determinista, o jogo ainda é muito desafiador.
Agora cabe ao leitor tornar o jogo ainda mais divertido. Existem vários recursos que podem ser adicionados:
  • Som
  • Pontuação máxima
  • IA avançada
  • Portais
  • Mais níveis
  • Vidas
  • Energizadores
  • Mais animações

Postar um comentário

4 Comentários

  1. Poderia me ajudar com o código do fantasma? ele não funciona. Obg

    ResponderExcluir
    Respostas
    1. Olá.
      Identifiquei que o blog alterou os "<" e ">" nos códigos por l&t e &gt.
      Já corrigi os códigos.
      Se tiver mais algum erro entre em contato.

      Excluir
  2. eu testei mesmo assim o fantasma só vai ate o primeiro waypoint.

    ResponderExcluir
    Respostas
    1. Oi Legoas.
      Testei e os scripts do ghost estão corretos. Verifique se todos os waypoints estão adicionados no inspector, como na imagem mostrada.
      https://noobtuts.com/content/unity/2d-pacman-game/blinky_ghostmove_withwaypoints.png

      Excluir
Emoji
(y)
:)
:(
hihi
:-)
:D
=D
:-d
;(
;-(
@-)
:P
:o
:>)
(o)
:p
(p)
:-s
(m)
8-)
:-t
:-b
b-(
:-#
=p~
x-)
(k)