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:
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 8 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:
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:
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:
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:
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
- direita
- esquerda
- cima
- baixo
Vamos
desenhar todas as animações em uma imagem, onde há uma animação por
linha:
Nota:
clique com o botão direito na imagem, selecione Salvar
como ... e
salve-a na pasta Assets / Sprites do
projeto .
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 .
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)
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) .
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:
Novamente,
precisamos apenas de uma máquina de estado de animação, então vamos
deletar as outras três:
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 right. Ele
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:
Criando os outros estados
O 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:
Esse
processo pode ser repetido nos dois estados restantes:
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:
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:
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:
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 right. Para
nos salvar de fazer todo esse trabalho, podemos usar o Any State do
Unity .
O 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 e 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:
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.
Nota: pressione o + no canto inferior direito para adicionar uma condição.
Vamos
também desabilitar o Can
Transition To Self nas configurações:
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:
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:
Se
pressionarmos Play ,
já poderemos ver a animação certa:
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):
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) :
// Commentusing UnityEngine;using System.Collections;public class PacmanMove : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }
A função Start é
chamada automaticamente pelo Unity ao iniciar o jogo. A 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.
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 .
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:
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 . A 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:
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:
Nota: clique com o botão direito na imagem, selecione Salvar como ... e salve-a na pasta Ativos / Sprites do projeto .
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.
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) .
Se
pressionarmos Play ,
o Pac-Man agora pode comê-los:
Sinta-se
à vontade para também mover todos os Pac-Dots para o
GameObject do labirinto:
Para
que possamos recolher o GameObject do labirinto para ter uma visão
limpa na hierarquia:
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:
Nota:
clique com o botão direito na imagem, selecione Salvar
como ... e
salve-o na pasta Ativos
/ Sprites do
projeto .
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:
Também
podemos excluir os arquivos
blinky_2 , blinky_4 e blinky_6 da pasta BlinkyAnimation 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:
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:
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:
Nós
também mudaremos a posição no Inspetor para
que Blinky esteja no meio do labirinto:
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:
Nota: habilitamos Is Trigger porque Blinky é um fantasma, e os fantasmas podem percorrer as coisas.
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:
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:
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; } }
// 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:
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:
Agora
podemos duplicar o Waypoint, renomeá-lo para Blinky_Waypoint1 e
posicioná-lo em (10,
20):
Então Blinky_Waypoint2 em (10, 14) :
E Blinky_Waypoint3 em (19, 14) :
E
finalmente Blinky_Waypoint4 em (19, 20) :
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:
Se
pressionarmos Play ,
poderemos ver como Blinky se move ao longo dos waypoints:
Naturalmente,
os waypoints atuais são bastante simples. Então,
fique à vontade para criar uma rota mais complexa como esta:
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
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:
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
4 Comentários
Poderia me ajudar com o código do fantasma? ele não funciona. Obg
ResponderExcluirOlá.
ExcluirIdentifiquei que o blog alterou os "<" e ">" nos códigos por l&t e >.
Já corrigi os códigos.
Se tiver mais algum erro entre em contato.
eu testei mesmo assim o fantasma só vai ate o primeiro waypoint.
ResponderExcluirOi Legoas.
ExcluirTestei 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