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.Atan2
para determinar o ângulo em radianos para o qual newDirection
direcionado (o ponto zero está à direita). Multiplica o resultado por 180 / 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
é timeInterval
que o timeBetweenWaves
. Caso contrário, verificamos se timeInterval
é timeInterval
que spawnInterval
ondas spawnInterval
. 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 dos enemiesSpawned
. - 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
CameraShake
para 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,
gameOver
como true
e inicie a animação gameOver
. - 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
OnEnemyDestroy
removemos o inimigo de enemiesInRange
. Quando um inimigo pisa em um gatilho em torno de um monstro, OnTriggerEnter2D
é OnTriggerEnter2D
. - Em seguida, adicionamos o inimigo à lista
enemiesInRange
e adicionamos o evento OnEnemyDestroy
. Portanto, garantimos que, após a destruição do inimigo, o OnEnemyDestroy
será chamado. Não queremos que os monstros gastem munição em inimigos mortos, certo? - No
OnTriggerExit2D
removemos 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>();
startTime
definimos 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.Lerp
para interpolação entre as posições inicial e final. - Se o projétil chegar
targetPosition
, verificaremos se ele ainda existe target
. - Obtemos o componente do
HealthBar
alvo e reduzimos sua saúde pelo tamanho do damage
projé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 MonsterLevel
seguintes 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 MonsterData
que 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 lastShotTime
valor da hora atual e obtemos acesso ao componente MonsterData
deste 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
bulletPrefab
apropriado MonsterLevel
. Designe startPosition
e targetPosition
projete. - 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
Shoot
se o tempo decorrido é maior que a frequência de disparo do monstro e definimos o lastShotTime
valor 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 .