Esta é a segunda parte do tutorial 
"Criando um jogo de Tower Defense no Unity" . Estamos criando um jogo de gênero de defesa de torre no Unity, e até o final da 
primeira parte , aprendemos como colocar e atualizar monstros. Também temos um inimigo atacando cookies.
No entanto, o inimigo ainda não sabe para onde olhar! Além disso, um ataque sozinho parece estranho. Nesta parte do tutorial, adicionaremos ondas de inimigos e monstros de braço para que eles possam defender um biscoito precioso.
Começando a trabalhar
Abra o projeto no Unity, que paramos na última parte. Se você se juntou a nós agora, faça o download do 
projeto preliminar e abra o 
TowerDefense-Part2-Starter .
Abra o 
GameScene na pasta 
Cenas .
Vire inimigos
No final do tutorial anterior, o inimigo aprendeu a se mover pela estrada, mas parece que ele não tem idéia de onde procurar.
Abra o script 
MoveEnemy.cs no IDE e adicione o seguinte método para corrigir a situação.
private void RotateIntoMoveDirection() {  
RotateIntoMoveDirection gira o inimigo para que ele sempre 
RotateIntoMoveDirection para a frente. Ele faz o seguinte:
- Calcula a direção atual do bug, subtraindo a posição do waypoint atual da posição do próximo ponto.
- Usa Mathf.Atan2para determinar o ângulo em radianos para o qualnewDirectiondirecionado (o ponto zero está à direita). Multiplica o resultado por180 / Mathf.PI, convertendo o ângulo em graus.
- Por fim, ele obtém o filho Sprite e gira o eixo de rotationAngleângulo. Observe que rotacionamos a criança , não o pai, de modo que a faixa de energia que adicionamos posteriormente permaneça horizontal.
Em 
Update() , substitua o comentário 
// TODO: próxima chamada para 
RotateIntoMoveDirection :
 RotateIntoMoveDirection(); 
Salve o arquivo e retorne ao Unity. Execute a cena; agora o inimigo sabe para onde está se movendo.
Agora o bug sabe para onde está indo.
O único inimigo não parece muito impressionante. Precisamos de hordas! E, como em qualquer jogo de defesa de torre, hordas correm em ondas!
Informar jogador
Antes de começarmos a mover as hordas, precisamos alertar o jogador sobre a batalha iminente. Além disso, vale a pena exibir o número da onda atual na parte superior da tela.
As informações de 
onda são exigidas por vários GameObjects, portanto, as adicionaremos ao componente 
GameManagerBehavior do 
GameManager .
Abra o 
GameManagerBehavior.cs no IDE e adicione as duas variáveis a seguir:
 public Text waveLabel; public GameObject[] nextWaveLabels; 
waveLabel armazena um link para a etiqueta de saída do número da onda no canto superior direito da tela. 
nextWaveLabels armazena dois GameObjects que criam uma combinação de animação que mostraremos no início de uma nova onda:
Salve o arquivo e retorne ao Unity. Selecione 
GameManager na 
Hierarquia . Clique no círculo à direita do 
rótulo da 
onda e, na caixa de diálogo 
Selecionar texto , selecione 
WaveLabel na guia 
Cena .
Agora defina o 
tamanho para 
etiquetas da próxima onda como 
2 . Agora defina o 
Elemento 0 como 
NextWaveBottomLabel e, para o 
Elemento 1, NextWaveTopLabel é o mesmo que fizemos com o Wave Label.
É assim que o Comportamento do Game Manager deve ser agoraSe o jogador perder, ele não verá uma mensagem sobre a próxima onda. Para lidar com essa situação, retorne ao 
GameManagerBehavior.cs e adicione outra variável:
 public bool gameOver = false; 
No 
gameOver armazenaremos o valor da perda do jogador.
Aqui, novamente usamos a propriedade para sincronizar os elementos do jogo com a onda atual. Adicione o seguinte código ao 
GameManagerBehavior :
 private int wave; public int Wave { get { return wave; } set { wave = value; if (!gameOver) { for (int i = 0; i < nextWaveLabels.Length; i++) { nextWaveLabels[i].GetComponent<Animator>().SetTrigger("nextWave"); } } waveLabel.text = "WAVE: " + (wave + 1); } } 
Criar uma variável privada, propriedade e getter já deve ser uma coisa familiar para você. Mas com o levantador, novamente, tudo é um pouco mais interessante.
Atribuímos a 
wave novo 
value .
Depois, verificamos se o jogo terminou. Caso contrário, percorra todos os rótulos 
nextWaveLabels - esses rótulos têm um componente 
Animator . Para ativar a animação do 
Animator , 
definimos um gatilho 
nextWave .
Por fim, definimos o 
text de 
waveLabel como 
wave + 1 . Por que 
+1 ? As pessoas comuns não começam a contar do zero (sim, isso é estranho).
Em 
Start() definimos o valor dessa propriedade:
 Wave = 0; 
Começamos a contagem com o número 
0 Wave .
Salve o arquivo e execute a cena no Unity. O rótulo do Wave mostrará corretamente 1.
Para um jogador, tudo começa com a onda 1.Ondas: crie montes de inimigos
Pode parecer óbvio, mas, para atacar com uma horda, é necessário criar mais inimigos - enquanto não sabemos como fazer isso. Além disso, não devemos criar a próxima onda até que a atual seja destruída.
Ou seja, o jogo deve ser capaz de reconhecer a presença de inimigos na cena, e as 
tags são uma boa maneira de identificar os objetos do jogo aqui.
Marcação inimiga
Selecione a pré-fabricada 
Inimigo no Project Browser. Na parte superior do 
Inspetor, clique na lista suspensa 
Marca e selecione 
Adicionar marca .
Crie uma 
tag chamada 
Inimigo .
Selecione o 
inimigo pré-fabricado. No 
Inspetor, defina 
a tag Inimigo para ele.
Definindo ondas de inimigos
Agora precisamos definir a onda de inimigos. Abra o 
SpawnEnemy.cs no IDE e adicione a seguinte implementação de classe antes do 
SpawnEnemy :
 [System.Serializable] public class Wave { public GameObject enemyPrefab; public float spawnInterval = 2; public int maxEnemies = 20; } 
Wave contém 
enemyPrefab - a base para criar instâncias de todos os inimigos nessa onda, 
spawnInterval - tempo entre os inimigos na onda em segundos e 
maxEnemies - o número de inimigos criados nessa onda.
A classe é 
serializável , ou seja, podemos alterar seus valores no Inspetor.
Adicione as seguintes variáveis à classe 
SpawnEnemy :
 public Wave[] waves; public int timeBetweenWaves = 5; private GameManagerBehavior gameManager; private float lastSpawnTime; private int enemiesSpawned = 0; 
Aqui, definimos as variáveis para gerar inimigos, o que é muito semelhante à forma como movemos os inimigos entre os pontos da rota.
Definimos as ondas de inimigos individuais em 
waves e rastreamos o número de inimigos criados e a hora em que foram criados em 
enemiesSpawned e 
lastSpawnTime .
Depois de todas essas mortes, os jogadores precisam de tempo para respirar, então defina 
timeBetweenWaves para 5 segundos.
Substitua o conteúdo de 
Start() seguinte código.
 lastSpawnTime = Time.time; gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>(); 
Aqui atribuímos 
lastSpawnTime valor do horário atual, ou seja, o horário em que o script foi iniciado após o carregamento da cena. Em seguida, obtemos o já conhecido 
GameManagerBehavior .
Adicione o seguinte código ao 
Update() :
 
Vamos analisá-lo passo a passo:
- Obtemos o índice da onda atual e verificamos se é a última.
- Nesse caso, calculamos o tempo decorrido após a geração anterior do inimigo e verificamos se é hora de criar um inimigo. Aqui levamos em conta dois casos. Se este é o primeiro inimigo na onda, verificamos se o timeIntervalétimeIntervalque otimeBetweenWaves. Caso contrário, verificamos setimeIntervalétimeIntervalquespawnIntervalondasspawnInterval. De qualquer forma, verificamos que não criamos todos os inimigos nesta onda.
- Se necessário, crie o inimigo, criando uma instância de enemyPrefab. Aumente também o valor dosenemiesSpawned.
- Verifique o número de inimigos na tela. Se eles não estão lá, e este foi o último inimigo na onda, criamos a próxima onda. Também no final da onda, damos ao jogador 10% de todo o ouro restante.
- Depois de derrotar a última onda, uma animação de vitória no jogo é jogada aqui.
Definir intervalos de reprodução
Salve o arquivo e retorne ao Unity. Selecione o objeto 
Estrada na 
Hierarquia . No 
Inspetor, defina o objeto 
Tamanho das 
ondas como 
4 .
Por enquanto, selecione um objeto 
Inimigo para todos os quatro elementos como 
Pré-fabricado Inimigo . Configure os campos 
Intervalo de geração e 
Inimigos máximos da seguinte maneira:
- Elemento 0 : Spawn Intervalo: 2,5 , Máximo Inimigos: 5
- Elemento 1 : Spawn Intervalo: 2 , Máx. Inimigos: 10
- Elemento 2 : Spawn Intervalo: 2 , Máx. Inimigos: 15
- Elemento 3 : Spawn Intervalo: 1 , Máx. Inimigos: 5
O esquema final deve ficar assim:
Obviamente, você pode experimentar esses valores para aumentar ou diminuir a complexidade.
Inicie o jogo. Sim! Os besouros começaram a jornada para o seu biscoito!
Tarefa adicional: adicione diferentes tipos de inimigos
Nenhum jogo de defesa de torre pode ser considerado completo com apenas um tipo de inimigo. Felizmente, também 
existe o 
Enemy2 na pasta 
Prefabs .
No 
Inspetor, selecione 
Prefabs \ Enemy2 e adicione o script 
MoveEnemy a ele. Defina 
Speed como 
3 e defina 
a tag Enemy . Agora você pode usar esse inimigo rápido para que o jogador não relaxe!
Atualização da vida do jogador
Mesmo que hordas de inimigos atacem o cookie, o jogador não sofre dano. Mas em breve vamos consertar. O jogador deve sofrer se permitir que o inimigo se esgueire.
Abra o 
GameManagerBehavior.cs no IDE e adicione as duas variáveis a seguir:
 public Text healthLabel; public GameObject[] healthIndicator; 
Usamos 
healthLabel para acessar o valor da vida do jogador e 
healthIndicator para acessar os cinco monstrinhos verdes que mastigam cookies - eles simplesmente simbolizam a saúde do jogador; é mais engraçado que um indicador de saúde padrão.
Gestão em saúde
Agora adicione uma propriedade que armazena a saúde do jogador no 
GameManagerBehavior :
 private int health; public int Health { get { return health; } set {  
É assim que gerenciamos a saúde do jogador. E, novamente, a parte principal do código está localizada no setter:
- Se reduzirmos a saúde do jogador, usamos o componente CameraShakepara criar um belo efeito de trepidação. Esse script está incluído no projeto para download e não será considerado aqui.
- Atualizamos a variável privada e o rótulo de integridade no canto superior esquerdo da tela.
- Se a saúde caiu para 0 e o final do jogo ainda não chegou, gameOvercomotruee inicie a animaçãogameOver.
- Removemos um dos monstros dos cookies. Se simplesmente desativá-los, essa parte pode ser escrita mais facilmente, mas aqui apoiamos a re-inclusão caso a integridade seja adicionada.
Inicializamos 
Health in 
Start() :
 Health = 5; 
Definimos 
Health como 
5 quando a cena começa a ser reproduzida.
Tendo feito tudo isso, agora podemos atualizar a saúde do jogador quando o bug chegar ao cookie. Salve o arquivo e acesse o IDE no script 
MoveEnemy.cs .
Mudança de saúde
Para alterar sua saúde, localize o comentário em 
Update() com as palavras 
// TODO: e substitua-o por este código:
 GameManagerBehavior gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>(); gameManager.Health -= 1; 
Portanto, obtemos o 
GameManagerBehavior e 
GameManagerBehavior a unidade de sua 
Health .
Salve o arquivo e retorne ao Unity.
Selecione um 
GameManager na 
Hierarquia e selecione 
HealthLabel para seu 
Rótulo de Vida .
Expanda o objeto 
Cookie na 
Hierarquia e arraste seus cinco 
HealthIndicators filhos para a 
matriz de indicadores de saúde do GameManager - os indicadores de saúde serão pequenos monstros verdes comendo biscoitos.
Execute a cena e aguarde até que os insetos atinjam o cookie. Não faça nada até perder.
Monster Revenge
Monstros no lugar? Sim Os inimigos atacam? Sim, e eles parecem ameaçadores! É hora de responder a esses animais!
Para fazer isso, precisamos do seguinte:
- Faixa de vida para que o jogador saiba quais inimigos são fortes e quais são fracos
- Detectando inimigos ao alcance de um monstro
- Tomar uma decisão - em qual inimigo atirar
- Um monte de conchas
Barra de saúde inimiga
Para implementar a faixa de saúde, usamos duas imagens - uma para o fundo escuro e a segunda (a barra verde é um pouco menor), escalaremos de acordo com a saúde do inimigo.
Arraste do 
Project Browser para a cena 
Prefabs \ Enemy .
Em seguida, na 
Hierarquia, arraste e solte 
Images \ Objects \ HealthBarBackground no 
Enemy para adicioná-lo como um filho.
No 
Inspetor, defina a 
Posição do HealthBarBackground como 
(0, 1, -4) .
Em seguida, no 
Navegador de projeto, selecione 
Images \ Objects \ HealthBar e verifique se o 
Pivot está à 
esquerda . Em seguida, adicione-o como filho do 
Inimigo na 
Hierarquia e defina seu valor de 
Posição (-0,63, 1, -5) . Para 
Escala X , defina o valor como 
125 .
Adicione um novo script 
C # chamado 
HealthBar ao objeto de jogo 
HealthBar . Mais tarde, vamos alterá-lo para que ele altere o comprimento da barra de saúde.
Depois de selecionar um objeto 
Inimigo na 
Hierarquia , verifique se sua posição é 
(20, 0, 0) .
Clique em 
Aplicar na parte superior do 
Inspetor para salvar todas as alterações como parte da pré-fabricada. Por fim, exclua o objeto 
Inimigo na 
Hierarquia .
Agora repita todas essas etapas para adicionar uma barra de 
integridade para 
Prefabs \ Enemy2 .
Alterar o comprimento da barra de integridade
Abra o IDE 
HealthBar.cs e adicione as seguintes variáveis:
 public float maxHealth = 100; public float currentHealth = 100; private float originalScale; 
No 
maxHealth a saúde máxima do inimigo é armazenada, e no 
currentHealth - o restante da saúde. Finalmente, em 
originalScale está o tamanho inicial da barra de integridade.
Salve o objeto 
originalScale em 
Start() :
 originalScale = gameObject.transform.localScale.x; 
Armazenamos o valor 
x da propriedade 
localScale .
Defina a escala da barra de integridade adicionando o seguinte código a 
Update() :
 Vector3 tmpScale = gameObject.transform.localScale; tmpScale.x = currentHealth / maxHealth * originalScale; gameObject.transform.localScale = tmpScale; 
Podemos copiar 
localScale em uma variável temporária porque não podemos alterar seu valor 
x separadamente. Depois calculamos a nova escala 
x com base na saúde atual do besouro e novamente atribuímos o valor 
localScale a uma variável temporária.
Salve o arquivo e inicie o jogo no Unity. Sobre os inimigos, você verá faixas de saúde.
Enquanto o jogo está em execução, expanda um dos objetos 
Inimigo (Clone) na 
Hierarquia e selecione sua 
barra de saúde filho. Altere seu valor de integridade 
atual e veja como a barra de integridade muda.
Detecção de inimigos dentro do alcance
Agora nossos monstros precisam descobrir quais inimigos apontar. Mas antes que você perceba essa oportunidade, você precisa preparar Monster e Inemy.
Selecione 
Prefabs do Project Browser 
\ Monster e adicione o componente 
2D do 
Circle Collider a ele no 
Inspector .
Defina o parâmetro 
Radius do colisor como 
2.5 - isso indicará o raio de ataque dos monstros.
Marque a caixa de seleção 
É acionador para que os objetos passem por essa área em vez de colidir com ela.
Finalmente, na parte superior do 
Inspetor , defina a 
Camada do monstro como 
Ignorar Raycast . Na caixa de diálogo, clique em 
Sim, altere filhos . Se Ignorar Raycast não estiver selecionado, o colisor responderá aos eventos de clique do mouse. Isso será um problema, porque os monstros bloqueiam eventos destinados aos objetos Openspot abaixo deles.
Para garantir que o inimigo seja detectado na área de gatilho, precisamos adicionar um colisor e um corpo rígido a ele, porque o Unity envia apenas eventos de gatilho quando um corpo rígido é anexado a um dos colisores.
No 
Navegador de projeto, selecione 
Prefabs \ Enemy . Adicione o componente 
Rigidbody 2D e selecione 
Kinematic para 
Body Type . Isso significa que o corpo não será afetado pela física.
Adicione 
Circle Collider 2D com um 
raio de 
1 . Repita essas etapas para 
Prefabs \ Enemy 2 .
Os gatilhos são configurados para que os monstros entendam que os inimigos estão dentro do seu raio de ação.
Precisamos preparar mais uma coisa: um script dizendo aos monstros quando o inimigo é destruído, para que eles não levantem uma exceção enquanto continuam atirando.
Crie um novo script 
C # chamado 
EnemyDestructionDelegate e adicione-o aos 
prefabs do 
Enemy e 
Enemy2 .
Abra 
EnemyDestructionDelegate.cs no IDE e adicione a seguinte declaração de delegação:
 public delegate void EnemyDelegate (GameObject enemy); public EnemyDelegate enemyDelegate; 
Aqui, criamos um 
delegate , ou seja, um contêiner para uma função que pode ser passada como uma variável.
Nota : os delegados são usados quando um objeto do jogo deve notificar ativamente outros objetos do jogo sobre alterações. Leia mais sobre os delegados na documentação do Unity .
Adicione o seguinte método:
 void OnDestroy() { if (enemyDelegate != null) { enemyDelegate(gameObject); } } 
Quando um objeto do jogo é destruído, o Unity chama automaticamente esse método e verifica o delegado quanto à desigualdade 
null . No nosso caso, chamamos isso de 
gameObject como parâmetro. Isso permite que todos os entrevistados registrados como delegados saibam que o inimigo está destruído.
Salve o arquivo e retorne ao Unity.
Damos aos monstros uma licença para matar
E agora os monstros podem detectar inimigos dentro do raio de sua ação. Adicione um novo script 
C # à pré-fabricada 
Monster e 
chame -a de 
ShootEnemies .
Abra 
ShootEnemies.cs no IDE e adicione o seguinte 
using construção para acessar os 
Generics .
 using System.Collections.Generic; 
Adicione uma variável para rastrear todos os inimigos dentro do alcance:
 public List<GameObject> enemiesInRange; 
Em 
enemiesInRange , armazenaremos todos os inimigos dentro do alcance.
Inicialize o campo em 
Start() .
 enemiesInRange = new List<GameObject>(); 
No começo, não há inimigos no raio de ação, por isso criamos uma lista vazia.
Preencha a lista de 
enemiesInRange ! Adicione o seguinte código ao script:
 
- No OnEnemyDestroyremovemos o inimigo deenemiesInRange. Quando um inimigo pisa em um gatilho em torno de um monstro,OnTriggerEnter2DéOnTriggerEnter2D.
- Em seguida, adicionamos o inimigo à lista enemiesInRangee adicionamos o eventoOnEnemyDestroy. Portanto, garantimos que, após a destruição do inimigo, oOnEnemyDestroyserá chamado. Não queremos que os monstros gastem munição em inimigos mortos, certo?
- No OnTriggerExit2Dremovemos o inimigo da lista e cancelamos o registro do delegado. Agora sabemos quais inimigos estão ao alcance.
Salve o arquivo e inicie o jogo no Unity. Para garantir que tudo esteja funcionando, posicione o monstro, selecione-o e siga as alterações na lista 
enemiesInRange no 
enemiesInRange .
Seleção de alvo
Os monstros agora sabem qual inimigo está ao alcance. Mas o que eles farão quando houver vários inimigos no raio?
Claro, eles atacarão o mais próximo do fígado!
Abra o script IDE 
MoveEnemy.cs e adicione um novo método que calcula esse monstro:
 public float DistanceToGoal() { float distance = 0; distance += Vector2.Distance( gameObject.transform.position, waypoints [currentWaypoint + 1].transform.position); for (int i = currentWaypoint + 1; i < waypoints.Length - 1; i++) { Vector3 startPosition = waypoints [i].transform.position; Vector3 endPosition = waypoints [i + 1].transform.position; distance += Vector2.Distance(startPosition, endPosition); } return distance; } 
O código calcula o comprimento do caminho ainda não percorrido pelo inimigo. Para fazer isso, ele usa 
Distance , calculado como a distância entre duas instâncias do 
Vector3 .
Usaremos esse método posteriormente para descobrir qual alvo atacar. No entanto, enquanto nossos monstros não estão armados e desamparados, primeiro faremos isso.
Salve o arquivo e retorne ao Unity para começar a configurar seus shells.
Vamos dar conchas aos monstros. Muitas conchas!
Arraste do Project Browser para a 
cena Images / Objects / Bullet1 . Defina a posição em 
z como 
-2 - as posições em xey não são importantes, porque as definimos sempre que criamos uma nova instância do projétil durante a execução do programa.
Adicione um novo script 
C # chamado 
BulletBehavior e, no IDE, adicione as seguintes variáveis:
 public float speed = 10; public int damage; public GameObject target; public Vector3 startPosition; public Vector3 targetPosition; private float distance; private float startTime; private GameManagerBehavior gameManager; 
speed determina a velocidade dos projéteis; o 
damage claro no nome.
target , 
targetPosition e 
targetPosition determinam a direção do projétil.
distance e 
startTime rastreiam a posição atual do projétil. 
gameManager recompensa o jogador quando ele mata o inimigo.
Atribua os valores dessas variáveis em 
Start() :
 startTime = Time.time; distance = Vector2.Distance (startPosition, targetPosition); GameObject gm = GameObject.Find("GameManager"); gameManager = gm.GetComponent<GameManagerBehavior>(); 
startTimedefinimos o valor do tempo atual e calculamos a distância entre as posições inicial e de destino. Além disso, como sempre, conseguimos GameManagerBehavior.Para controlar o movimento do projétil, adicione o Update()seguinte código: 
- Calculamos a nova posição do projétil, usando Vector3.Lerppara interpolação entre as posições inicial e final.
- Se o projétil chegar targetPosition, verificaremos se ele ainda existetarget.
- Obtemos o componente do HealthBaralvo e reduzimos sua saúde pelo tamanho dodamageprojétil.
- Se a saúde do inimigo for reduzida a zero, nós a destruímos, reproduzimos o efeito sonoro e recompensamos o jogador pela precisão.
Salve o arquivo e retorne ao Unity.Fazemos conchas grandes
Não seria ótimo se o monstro começasse a disparar mais projéteis em níveis altos? Felizmente, isso é fácil de implementar.Arraste o objeto de jogo Bullet1 da Hierarquia para a guia Projeto para criar uma pré-fabricada de projétil. Remova o objeto original da cena - não será mais necessário.Duplique o pref1 Bullet1 duas vezes . Nomeie as cópias de Bullet2 e Bullet3 .Selecione Bullet2 . No Inspetor, defina o campo Sprite do componente Sprite Renderer como Images / Objects / Bullet2. Então, tornaremos o Bullet2 um pouco mais que o Bullet1.Repita o procedimento para alterar o sprite pré-fabricada bullet3 em Imagens relacionadas / Objetos / bullet3 .Além disso, em Comportamento de balas , ajustaremos a quantidade de danos causados por projéteis.Selecione o marcador pré - fabricado Bullet1 na guia Projeto . No Inspetor, você verá Comportamento do marcador (Script) , no qual é possível definir Dano para 10 no marcador1 , 15 no marcador2 e 20 no marcador3 - ou quaisquer outros valores que você desejar.Nota : Alterei os valores para que, em níveis mais altos, o preço do dano se torne mais alto. Isso impede que a atualização permita que o jogador atualize monstros nos melhores pontos.
Cascas pré-fabricadas - o tamanho aumenta com o nívelAlterando o nível de conchas
Atribua conchas diferentes para diferentes níveis de monstros, para que monstros mais fortes destruam os inimigos mais rapidamente.Abra MonsterData.cs no IDE e adicione às MonsterLevelseguintes variáveis: public GameObject bullet; public float fireRate; 
Então, definimos o pré-fabricado do projétil e a frequência de tiro para cada nível de monstros. Salve o arquivo e retorne ao Unity para concluir a configuração do monstro.Selecione a pré-fabricada Monster no Project Browser . No Inspetor, expanda Níveis no componente Monster Data (Script) . Defina a taxa de incêndio de cada item como 1 . Em seguida, defina o parâmetro Bullet do elemento 0, 1 e 2 como Bullet1 , Bullet2 e Bullet3 .Os níveis de monstro devem ser definidos da seguinte forma:Conchas matam inimigos? Sim Vamos abrir o fogo!Fogo aberto
Abra ShootEnemies.cs no IDE e adicione as seguintes variáveis: private float lastShotTime; private MonsterData monsterData; 
Como o nome indica, essas variáveis rastreiam o tempo do último tiro do monstro, bem como a estrutura MonsterDataque contém informações sobre o tipo de projétil de monstro, a frequência do disparo e assim por diante.Defina os valores desses campos em Start(): lastShotTime = Time.time; monsterData = gameObject.GetComponentInChildren<MonsterData>(); 
Aqui, atribuímos o lastShotTimevalor da hora atual e obtemos acesso ao componente MonsterDatadeste objeto.Adicione o seguinte método para implementar o disparo: void Shoot(Collider2D target) { GameObject bulletPrefab = monsterData.CurrentLevel.bullet;  
- Temos as posições inicial e alvo da bala. Defina a posição z igual a z bulletPrefab. Anteriormente, definimos a posição pré-fabricada do projétil em z, para que o projétil apareça sob o monstro atirador, mas acima dos inimigos.
- Criamos uma instância de um novo shell usando o bulletPrefabapropriadoMonsterLevel. DesignestartPositionetargetPositionprojete.
- Tornamos o jogo mais interessante: quando o monstro disparar, inicie a animação do disparo e toque o som do laser.
Juntando tudo
É hora de juntar tudo. Defina o alvo e faça o monstro olhar para ele.No script ShootEnemies.cs, adicione a Update()este código: GameObject target = null;  
Considere este código passo a passo.- Determine o propósito do monstro. Começamos na distância máxima possível em minimalEnemyDistance. Percorremos um ciclo de todos os inimigos dentro do alcance e transformamos o inimigo em um novo alvo, se a distância dele para o biscoito for menor que a menor atual.
- Chamamos Shootse o tempo decorrido é maior que a frequência de disparo do monstro e definimos olastShotTimevalor do tempo atual.
- Calculamos o ângulo de rotação entre o monstro e seu alvo. Nós giramos o monstro para esse ângulo. Agora ele sempre olha para o alvo.
Salve o arquivo e inicie o jogo no Unity. Monstros começarão a proteger desesperadamente os cookies. Finalmente terminamos!Para onde ir a seguir
O projeto finalizado pode ser baixado aqui .Fizemos um ótimo trabalho neste tutorial e agora temos um ótimo jogo.Aqui estão algumas idéias para um maior desenvolvimento do projeto:- Mais tipos de inimigos e monstros
- Diferentes rotas de inimigos
- Níveis de jogo diferentes
Cada um desses aspectos exigirá mudanças mínimas e pode tornar o jogo mais divertido. Se você criar um novo jogo com base neste tutorial, será um prazer jogá-lo, então compartilhe um link para ele.Pensamentos interessantes sobre a criação de um jogo de sucesso de defesa de torre podem ser encontrados nesta entrevista .