Fazendo da Defesa da Torre um Jogo de Unidade - Parte 1

imagem

Os jogos de defesa de torre estão ganhando popularidade, e isso não é surpreendente - pouco pode ser comparado com o prazer de observar suas próprias linhas de defesa que destroem inimigos do mal! Neste tutorial em duas partes, criaremos um jogo de defesa de torre no mecanismo Unity !

Você aprenderá como fazer o seguinte:

  • Crie ondas de inimigos
  • Faça-os seguir pontos de rota
  • Construa e atualize torres e também ensine-os a quebrar inimigos em pequenos pixels

No final, obtemos o quadro do jogo, que pode ser desenvolvido ainda mais!

Nota : você precisa de conhecimentos básicos do Unity (por exemplo, precisa saber como ativos e componentes são adicionados, o que são prefabs) e os conceitos básicos de C # . Para aprender tudo isso, recomendo que você siga os tutoriais sobre Unity de Sean Duffy ou a série Beginning C # with Unity de Brian Mockley.

Vou trabalhar no Unity para OS X, mas este tutorial também é adequado para Windows.

Através das janelas da torre de marfim


Neste tutorial, criaremos um jogo de defesa de torre no qual inimigos (pequenos bugs) rastejam para um cookie pertencente a você e a seus lacaios (é claro, esses são monstros!). O jogador pode colocar monstros em pontos estratégicos e atualizá-los em ouro.

O jogador deve matar todos os bugs até chegar ao cookie. Cada nova onda de inimigos está se tornando cada vez mais difícil de derrotar. O jogo termina quando você sobrevive a todas as ondas (vitória!) Ou quando cinco inimigos rastejam até os biscoitos (perda!).

Aqui está uma captura de tela do jogo finalizado:


Monstros, uni-vos! Proteja o biscoito!

Começando a trabalhar


Faça o download deste projeto em branco , descompacte-o e abra o projeto TowerDefense-Part1-Starter no Unity.

O projeto de rascunho possui recursos de gráficos e sons, animações prontas e vários scripts úteis. Os scripts não estão diretamente relacionados aos jogos de defesa, então não vou falar sobre eles aqui. No entanto, se você quiser saber mais sobre a criação de animações 2D no Unity, confira este tutorial do Unity 2D .

O projeto também contém prefabs, que adicionaremos posteriormente para criar caracteres. Finalmente, há uma cena no projeto com um plano de fundo e uma interface de usuário personalizada.

Abra o GameScene localizado na pasta Cenas e defina o modo Jogo com uma proporção de 4: 3 para que todos os rótulos correspondam corretamente ao plano de fundo. No modo de jogo, você verá o seguinte:


Autoria:

  • Os gráficos do projeto foram retirados do pacote Wiki Wenderlich grátis! Outros trabalhos gráficos podem ser encontrados em seu site gameartguppy .
  • Ótima música tirada do BenSound , que tem outras trilhas sonoras incríveis!
  • Agradeço também a Michael Jesper pela muito útil função de trepidação da câmera.
.

O local está marcado com uma cruz: a localização dos monstros


Monstros só podem ser colocados em pontos marcados com um x .

Para adicioná-los à cena, arraste Images \ Objects \ Openspot do Project Browser para a janela Scene . Enquanto a posição não é importante para nós.

Depois de selecionar o Openspot na hierarquia, clique em Adicionar componente no Inspetor e selecione Box Collider 2D . Na janela Cena, o Unity exibirá um colisor retangular com uma linha verde. Usaremos esse colisor para reconhecer os cliques do mouse neste local.


Adicione o componente Audio \ Audio Source ao Openspot da mesma maneira. Para o parâmetro AudioClip do componente Audio Source, selecione o arquivo tower_place localizado na pasta Audio e desative Play On Awake .

Precisamos criar mais 11 pontos. Embora exista uma tentação de repetir todas essas etapas, o Unity tem uma solução melhor: Prefab !

Arraste Openspot da hierarquia para a pasta Prefabs dentro do Project Browser . Seu nome ficará azul na Hierarquia, o que significa que está anexado à pré-fabricada. Algo assim:


Agora que temos o espaço em branco pré-fabricado, podemos criar quantas cópias quisermos. Basta arrastar e soltar o Openspot da pasta Prefabs dentro do Project Browser na janela Scene . Repita isso 11 vezes e 12 objetos Openspot aparecerão na cena.

Agora use o Inspetor para definir esses 12 objetos Openspot com as seguintes coordenadas:

  • (X: -5,2, Y: 3,5, Z: 0)
  • (X: -2,2, Y: 3,5, Z: 0)
  • (X: 0,8, Y: 3,5, Z: 0)
  • (X: 3,8, Y: 3,5, Z: 0)
  • (X: -3,8, Y: 0,4, Z: 0)
  • (X: -0,8, Y: 0,4, Z: 0)
  • (X: 2,2, Y: 0,4, Z: 0)
  • (X: 5,2, Y: 0,4, Z: 0)
  • (X: -5,2, Y: -3,0, Z: 0)
  • (X: -2,2, Y: -3,0, Z: 0)
  • (X: 0,8, Y: -3,0, Z: 0)
  • (X: 3,8, Y: -3,0, Z: 0)

Quando você fizer isso, a cena ficará assim:


Colocamos monstros


Para simplificar o posicionamento, há uma pré-fabricada Monster na pasta Pré - fabricada do projeto.


Monster Prefab pronto para uso

No momento, ele consiste em um objeto de jogo vazio com três sprites diferentes e animações de tiro quando crianças.

Cada sprite é um monstro com diferentes níveis de poder. A pré-fabricada também contém o componente Fonte de áudio , que será lançado para reproduzir som quando um monstro disparar um laser.

Agora vamos criar um script que hospedará o Monster no Openspot .

No Navegador de projeto, selecione o objeto Openspot na pasta Prefabs . No Inspetor, clique em Adicionar componente e selecione Novo script e nomeie o script como PlaceMonster . Selecione C Sharp como o idioma e clique em Criar e adicionar . Como adicionamos o script à pré-fabricada Openspot , todos os objetos Openspot na cena agora terão esse script. Ótimo!

Clique duas vezes no script para abri-lo no IDE. Em seguida, adicione duas variáveis:

public GameObject monsterPrefab; private GameObject monster; 

Vamos criar uma instância do objeto armazenado no monsterPrefab para criar o monstro e armazená-lo no monster para que ele possa ser manipulado durante o jogo.

Um monstro por ponto


Para que apenas um monstro possa ser colocado em um ponto, adicione o seguinte método:

 private bool CanPlaceMonster() { return monster == null; } 

Em CanPlaceMonster() podemos verificar se a variável monster ainda é null . Nesse caso, não há monstro no momento, e podemos colocá-lo.

Agora adicione o seguinte código para colocar o monstro quando o jogador clicar neste GameObject:

 //1 void OnMouseUp() { //2 if (CanPlaceMonster()) { //3 monster = (GameObject) Instantiate(monsterPrefab, transform.position, Quaternion.identity); //4 AudioSource audioSource = gameObject.GetComponent<AudioSource>(); audioSource.PlayOneShot(audioSource.clip); // TODO:   } } 

Este código localiza o monstro quando você clica no mouse ou toca na tela. Como ele trabalha?

  1. O Unity chama automaticamente o OnMouseUp quando um jogador toca no colisor físico GameObject.
  2. Quando chamado, esse método coloca um monstro se CanPlaceMonster() retornar true .
  3. Criamos um monstro usando o método Instantiate , que cria uma instância da pré-fabricada especificada com a posição e rotação especificadas. Nesse caso, monsterPrefab , fornecemos a posição atual do GameObject sem rotação, transferimos o resultado para o GameObject e salvamos no monster
  4. No final, chamamos PlayOneShot para reproduzir o efeito sonoro anexado ao componente AudioSource do objeto.

Agora, nosso script do PlaceMonster pode ter um novo monstro, mas ainda precisamos especificar uma pré-fabricada.

Usando o Prefab certo


Salve o arquivo e retorne ao Unity.

Para definir a variável monsterPrefab , primeiro selecione o objeto Openspot na pasta Prefabs no navegador do projeto.

No Inspetor, clique no círculo à direita do campo Monster Prefab do componente PlaceMonster (Script) e selecione Monster na caixa de diálogo exibida.


Isso é tudo. Inicie a cena e crie monstros em lugares diferentes, clicando com o mouse ou tocando na tela.


Ótimo! Agora podemos criar monstros. No entanto, eles parecem uma bagunça estranha, porque todos os sprites infantis do monstro são atraídos. Agora vamos consertar isso.

Aumentar o nível de monstros


A figura abaixo mostra que, com um aumento de nível, os monstros parecem cada vez mais assustadores.


Que gracinha! Mas se você tentar roubar seus biscoitos, esse monstro se tornará um assassino.

O script é usado como base para a implementação do sistema de níveis de monstros. Ele rastreia o poder do monstro em cada nível e, é claro, o nível atual do monstro.

Adicione este script.

Selecione a pré-fabricada / Pré- fabricada / Monster no Project Browser . Adicione um novo script C # chamado MonsterData . Abra o script no IDE e adicione o seguinte código acima da classe MonsterData .

 [System.Serializable] public class MonsterLevel { public int cost; public GameObject visualization; } 

Então criamos o MonsterLevel . Ele agrupa o preço (em ouro, que iremos apoiar abaixo) e uma representação visual do nível do monstro.

Adicionamos em cima de [System.Serializable] para que instâncias de classe possam ser modificadas no inspetor. Isso nos permite alterar rapidamente todos os valores da classe Level, mesmo quando o jogo está rodando. Isso é incrivelmente útil para equilibrar o jogo.

Definir níveis de monstro


No nosso caso, armazenaremos o MonsterLevel especificado na List<T> .

Por que não usar o MonsterLevel[] ? Vamos precisar do índice de um objeto MonsterLevel específico várias vezes. Embora seja fácil escrever código para isso, ainda precisamos usar IndexOf() , que implementa a funcionalidade Lists . Não faz sentido reinventar a roda.


Reinventar a bicicleta geralmente é uma má idéia.

Na parte superior do MonsterData.cs, adicione o seguinte using construção:

 using System.Collections.Generic; 

Isso nos dá acesso a estruturas de dados generalizadas para que possamos usar a classe List<T> no script.

Nota : generalizações são um poderoso conceito de C #. Eles permitem que você especifique estruturas de dados com segurança de tipo sem ter que aderir ao tipo. Isso é útil para classes de contêineres, como listas e conjuntos. Para saber mais sobre estruturas genéricas, leia o livro Introdução aos C # Genéricos .

Agora adicione a seguinte variável ao MonsterData para manter a lista MonsterLevel :

 public List<MonsterLevel> levels; 

Graças a generalizações, podemos garantir que a List do level contenha apenas objetos MonsterLevel .

Salve o arquivo e mude para o Unity para configurar cada nível.

Selecione Prefabs / Monster no Project Browser . O Inspetor agora exibe o campo Níveis do componente MonsterData (Script) . Defina o tamanho como 3 .


Em seguida, defina o custo para cada nível:

  • Elemento 0 : 200
  • Elemento 1 : 110
  • Elemento 2 : 120

Agora, atribuímos os valores dos campos de exibição visual.

Expanda Prefabs / Monster no navegador do Projeto para ver seus filhos. Arraste o filho Monster0 para o campo de elemento de visualização 0 .

Em seguida, defina o Elemento 1 como Monster1 e o Elemento 2 como Monster2 . O GIF mostra este processo:


Quando você seleciona Prefabs / Monster , a pré-fabricada deve ficar assim:


Definir nível atual


Volte para MonsterData.cs no IDE e adicione outra variável ao MonsterData .

 private MonsterLevel currentLevel; 

Na variável privada currentLevel , armazenaremos o nível atual do monstro.

Agora defina currentLevel e torne-o visível para outros scripts. Adicione as seguintes linhas ao MonsterData juntamente com a declaração das variáveis ​​da instância:

 //1 public MonsterLevel CurrentLevel { //2 get { return currentLevel; } //3 set { currentLevel = value; int currentLevelIndex = levels.IndexOf(currentLevel); GameObject levelVisualization = levels[currentLevelIndex].visualization; for (int i = 0; i < levels.Count; i++) { if (levelVisualization != null) { if (i == currentLevelIndex) { levels[i].visualization.SetActive(true); } else { levels[i].visualization.SetActive(false); } } } } } 

Grande parte do código C #, certo? Vamos tomá-lo em ordem:

  1. Defina a propriedade da variável privada currentLevel . Ao definir a propriedade, podemos chamá-la como qualquer outra variável: como CurrentLevel (dentro da classe) ou como monster.CurrentLevel (fora). Podemos definir qualquer comportamento no método getter ou setter de uma propriedade e, criando apenas um getter, setter ou ambos, você pode controlar as propriedades da propriedade: somente leitura, somente leitura e gravação / leitura.
  2. No getter, retornamos o valor de currentLevel .
  3. No setter, atribuímos a currentLevel novo valor. Em seguida, obtemos o índice do nível atual. Por fim, percorremos todos os níveis e ativamos / desativamos a exibição visual, dependendo do currentLevelIndex . Isso é ótimo porque, quando o currentLevel muda, o sprite é atualizado automaticamente. Propriedades são uma coisa muito conveniente!

Adicione a seguinte implementação OnEnable :

 void OnEnable() { CurrentLevel = levels[0]; } 

Aqui configuramos CurrentLevel ao colocar. Isso garante que apenas o sprite desejado seja exibido.

Nota : é importante inicializar a propriedade em OnEnable , e não em OnStart , porque chamamos os métodos ordinais ao criar instâncias pré-fabricadas.

OnEnable será chamado imediatamente quando a pré-fabricada for criada (se a pré-fabricada tiver sido salva no estado ativado), mas o OnStart não OnStart chamado até que o objeto comece a ser executado como parte da cena.

Precisamos verificar esses dados antes de colocar o monstro, então inicializamos para OnEnable .

Salve o arquivo e retorne ao Unity. Execute o projeto e coloque os monstros; agora eles exibem os sprites corretos do nível mais baixo.


Monster Upgrade


Retorne ao IDE e adicione o seguinte método ao MonsterData :

 public MonsterLevel GetNextLevel() { int currentLevelIndex = levels.IndexOf (currentLevel); int maxLevelIndex = levels.Count - 1; if (currentLevelIndex < maxLevelIndex) { return levels[currentLevelIndex+1]; } else { return null; } } 

Em GetNextLevel , obtemos o índice currentLevel e o índice de nível mais alto; se o monstro não atingiu o nível máximo, o próximo nível retornará. Caso contrário, null retornado.

Você pode usar este método para descobrir se é possível uma atualização de monstro.

Para aumentar o nível do monstro, adicione o seguinte método:

 public void IncreaseLevel() { int currentLevelIndex = levels.IndexOf(currentLevel); if (currentLevelIndex < levels.Count - 1) { CurrentLevel = levels[currentLevelIndex + 1]; } } 

Aqui obtemos o índice do nível atual e, em seguida, certifique-se de que este não seja o nível máximo, verificando se é menor que os levels.Count - 1 . CurrentLevel caso, CurrentLevel para o próximo nível.

Verificando a funcionalidade de atualização


Salve o arquivo e retorne para PlaceMonster.cs no IDE. Adicione um novo método:

 private bool CanUpgradeMonster() { if (monster != null) { MonsterData monsterData = monster.GetComponent<MonsterData>(); MonsterLevel nextLevel = monsterData.GetNextLevel(); if (nextLevel != null) { return true; } } return false; } 

Primeiro, verificamos se há um monstro que pode ser melhorado comparando a variável monster com null . Se isso for verdade, obtemos o nível de monstro atual de seus MonsterData .

Em seguida, verificamos se o próximo nível está disponível, ou seja, se GetNextLevel() não retorna null . Se um aumento de nível for possível, retornamos true ; caso contrário, retorne false .

Implementamos melhorias para o ouro


Para habilitar a opção de atualização, adicione a ramificação else if ao OnMouseUp :

 if (CanPlaceMonster()) { //      } else if (CanUpgradeMonster()) { monster.GetComponent<MonsterData>().IncreaseLevel(); AudioSource audioSource = gameObject.GetComponent<AudioSource>(); audioSource.PlayOneShot(audioSource.clip); // TODO:   } 

Verificamos a possibilidade de uma atualização usando CanUpgradeMonster() . Se possível, MonsterData componente MonsterData usando GetComponent() e chamamos IncreaseLevel() , o que aumenta o nível do monstro. Finalmente, lançamos o Monster AudioSource .

Salve o arquivo e retorne ao Unity. Execute o jogo, coloque e atualize qualquer número de monstros (mas por enquanto).


Pagando Ouro - Game Manager


Embora possamos construir e melhorar imediatamente qualquer monstro, será interessante no jogo?

Vejamos a questão do ouro. O problema com o rastreamento é que precisamos transferir informações entre diferentes objetos do jogo.

A figura abaixo mostra todos os objetos que devem participar disso.


Todos os objetos de jogo selecionados devem saber quanto ouro um jogador possui.

Para armazenar esses dados, usaremos um objeto comum que outros objetos podem acessar.

Clique com o botão direito do mouse na Hierarquia e selecione Criar Vazio . Nomeie o novo objeto GameManager .

Adicione um novo script C # chamado GameManagerBehavior ao GameManager e abra-o no IDE. Vamos exibir a quantidade total de ouro do jogador no rótulo, portanto, na parte superior do arquivo, adicione a seguinte linha:

 using UnityEngine.UI; 

Isso nos permitirá acessar classes de interface do usuário, como Text , que é usado para rótulos. Agora adicione a seguinte variável à classe:

 public Text goldLabel; 

Ele armazenará um link para o componente Text usado para exibir a quantidade de ouro que um jogador possui.

Agora que o GameManager conhece o rótulo, como sincronizamos a quantidade de ouro armazenada na variável e o valor exibido no rótulo? Vamos criar uma propriedade.

Adicione o seguinte código ao GameManagerBehavior :

 private int gold; public int Gold { get { return gold; } set { gold = value; goldLabel.GetComponent<Text>().text = "GOLD: " + gold; } } 

Ele parece familiar? O código é semelhante ao CurrentLevel , que definimos no Monster . Primeiro, criamos uma variável privada gold para armazenar a quantidade atual de ouro. Em seguida, definimos a propriedade Gold (inesperadamente, certo?) E implementamos o getter e o setter.

O getter simplesmente retorna o valor do gold . O levantador é mais interessante. Além de definir o valor da variável, também define o campo de text para goldLabel para exibir o novo valor de ouro.

Quão generosos seremos? Adicione a seguinte linha ao Start() para dar ao jogador 1000 gold ou menos se você sentir pena do dinheiro:

 Gold = 1000; 

Atribuindo um objeto de rótulo a um script


Salve o arquivo e retorne ao Unity. Na Hierarquia, selecione GameManager . No Inspetor, clique no círculo à direita do rótulo dourado . Na caixa de diálogo Selecionar texto , selecione a guia Cena e selecione GoldLabel .


Execute a cena e o rótulo exibirá Ouro: 1000 .


Verificando a "carteira" do jogador


Abra o script PlaceMonster.cs no IDE e adicione a seguinte variável de instância:

 private GameManagerBehavior gameManager; 

Usaremos o gameManager para acessar o componente GameManagerBehavior objeto GameManagerBehavior na cena. Para especificá-lo, adicione o seguinte a Start() :

 gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>(); 

Obtemos um GameObject chamado GameManager usando a função GameObject.Find GameObject.Find() , que retorna o primeiro objeto de jogo encontrado com esse nome. Em seguida, obtemos seu componente GameManagerBehavior e o salvamos para o futuro.

Nota : você pode fazer isso definindo um campo no editor do Unity ou adicionando um método estático ao GameManager que retorna uma instância do singleton a partir da qual podemos obter o GameManagerBehavior .

No entanto, no bloco de código mostrado acima, há uma confusão: o método Find , que funciona mais lentamente durante a execução do aplicativo; mas é conveniente e pode ser usado com moderação.

Pegue meu dinheiro!


Ainda não OnMouseUp() ouro, portanto, adicionaremos essa linha duas vezes ao OnMouseUp() , substituindo cada um dos comentários // TODO: :

 gameManager.Gold -= monster.GetComponent<MonsterData>().CurrentLevel.cost; 

Salve o arquivo e volte para o Unity, atualize alguns monstros e veja a atualização do valor Gold. Agora deduzimos ouro, mas os jogadores podem construir monstros desde que tenham espaço suficiente; eles apenas pedem dinheiro emprestado.


Crédito infinito? Ótimo! Mas não podemos permitir. O jogador deve poder apostar em monstros enquanto tiver ouro suficiente.

Verificação de ouro para monstros


Alterne no IDE para PlaceMonster.cs e substitua o conteúdo de CanPlaceMonster() seguinte:

 int cost = monsterPrefab.GetComponent<MonsterData>().levels[0].cost; return monster == null && gameManager.Gold >= cost; 

Nós MonsterData preço de colocação de monstros nos levels em seus MonsterData . Depois, verificamos que o monster não monster null e que gameManager.Gold mais do que esse preço.

A tarefa para você: adicione independentemente ao CanUpgradeMonster() verificação se o jogador possui ouro suficiente.

Solução dentro
Substitua a linha:

 return true; 

sobre isso:

 return gameManager.Gold >= nextLevel.cost; 

Ele verificará se o jogador tem mais ouro do que o preço de atualização.

Salve e execute a cena no Unity. Agora tente aqueles como adicionar monstros ilimitadamente!


Agora só podemos construir um número limitado de monstros.

Política da torre: inimigos, ondas e pontos de referência


É hora de "abrir o caminho" para nossos inimigos. Os inimigos aparecem no primeiro ponto da rota, passam para o próximo e repetem o processo até atingirem o cookie.

Você pode fazer os inimigos se moverem assim:

  1. Defina o caminho que os inimigos seguirão
  2. Mova o inimigo pela estrada
  3. Vire o inimigo para que ele olhe para a frente

Criando uma estrada a partir de waypoints


Clique com o botão direito do mouse na Hierarquia e selecione Criar Vazio para criar um novo objeto de jogo vazio. Nomeie-o como Estrada e posicione-o em (0, 0, 0) .

Agora clique com o botão direito do mouse em Road in the Hierarchy e crie outro objeto de jogo vazio como filho de Road. Nomeie-o como Waypoint0 e coloque-o no ponto (-12, 2, 0) - a partir daqui os inimigos começarão seu movimento.


Da mesma forma, crie mais cinco pontos de rota com os seguintes nomes e posições:

  • Ponto de passagem1: (X: 7, Y: 2, Z: 0)
  • Waypoint2: (X: 7, Y: -1, Z: 0)
  • Ponto de referência3: (X: -7.3, Y: -1, Z: 0)
  • Ponto de referência4: (X: -7.3, Y: -4.5, Z: 0)
  • Ponto de referência5: (X: 7, Y: -4,5, Z: 0)

A captura de tela abaixo mostra os pontos da rota e o caminho resultante.


Fazendo inimigos


Agora crie alguns inimigos para que eles possam se mover pela estrada. Há uma pré- fabricação inimiga na pasta Prefabs . Sua posição é (-20, 0, 0) , portanto, novas instâncias serão criadas fora da tela.

Em todos os outros aspectos, ele é configurado quase da mesma maneira que o pré-fabricado Monster, possui o AudioSource e uma subsidiária da Sprite , e podemos rotacionar esse sprite no futuro sem virar a barra de integridade.


Movemos inimigos ao longo da estrada


Adicione um novo script C # chamado MoveEnemy à prefab Prefabs \ Enemy . Abra o script no IDE e adicione as seguintes variáveis:

 [HideInInspector] public GameObject[] waypoints; private int currentWaypoint = 0; private float lastWaypointSwitchTime; public float speed = 1.0f; 

Nos waypoints , uma cópia dos pontos de rota é armazenada na matriz, e a linha [HideIn inspector ] acima dos waypoints garante que não possamos alterar acidentalmente esse campo no Inspector , mas ainda assim teremos acesso a partir de outros scripts.

currentWaypoint rastreia a localização da rota do inimigo no momento atual e lastWaypointSwitchTime armazena o tempo que o inimigo passou por ela. Além disso, armazenamos a speed inimigo.

Adicione esta linha ao Start() :

 lastWaypointSwitchTime = Time.time; 

Portanto, inicializamos lastWaypointSwitchTime com o valor do horário atual.

Para que o inimigo se mova ao longo da rota, adicione o seguinte código a Update() :

 // 1 Vector3 startPosition = waypoints [currentWaypoint].transform.position; Vector3 endPosition = waypoints [currentWaypoint + 1].transform.position; // 2 float pathLength = Vector3.Distance (startPosition, endPosition); float totalTimeForPath = pathLength / speed; float currentTimeOnPath = Time.time - lastWaypointSwitchTime; gameObject.transform.position = Vector2.Lerp (startPosition, endPosition, currentTimeOnPath / totalTimeForPath); // 3 if (gameObject.transform.position.Equals(endPosition)) { if (currentWaypoint < waypoints.Length - 2) { // 3.a currentWaypoint++; lastWaypointSwitchTime = Time.time; // TODO:     } else { // 3.b Destroy(gameObject); AudioSource audioSource = gameObject.GetComponent<AudioSource>(); AudioSource.PlayClipAtPoint(audioSource.clip, transform.position); // TODO:   } } 

Vamos analisar o código passo a passo:

  1. A partir da matriz de pontos de rota, obtemos as posições inicial e final do segmento de rota atual.
  2. Calculamos o tempo necessário para percorrer toda a distância usando a fórmula tempo = distância / velocidade e, em seguida, determinamos a hora atual na rota. Usando Vector2.Lerp , interpolamos a posição atual do inimigo entre o segmento exato inicial e final.
  3. Verifique se o inimigo atingiu endPosition . Se sim, então processamos dois cenários possíveis:
    1. O inimigo ainda não atingiu o último ponto da rota, então aumente o valor do currentWaypoint e atualize lastWaypointSwitchTime . Mais tarde, adicionaremos um código para transformar o inimigo, para que ele olhe na direção de seu movimento.
    2. O inimigo atingiu o último ponto da rota, depois a destruímos e iniciamos o efeito sonoro. Mais tarde, adicionaremos um código que reduz a health do jogador.

Salve o arquivo e retorne ao Unity.

Informamos os inimigos da direção do movimento


No estado atual, os inimigos não sabem a ordem dos pontos da rota.

Selecione Estrada na Hierarquia e adicione um novo script C # chamado SpawnEnemy . Abra-o no IDE e adicione a seguinte variável:

 public GameObject[] waypoints; 

Usaremos waypoints para armazenar referências ao waypoint na cena na ordem desejada.

Salve o arquivo e retorne ao Unity. Selecione Estrada na Hierarquia e defina o tamanho da matriz de Waypoints como 6 .

Arraste cada um dos filhos da Estrada para os campos colando Waypoint0 no Elemento 0 , Waypoint1 no Elemento 1 e assim por diante.


Agora temos uma matriz que contém os pontos de rota na ordem correta - lembre-se, os inimigos nunca recuam, eles persistentemente lutam por uma recompensa doce.

Veja como tudo funciona


Abra SpawnEnemy no IDE e adicione a seguinte variável:

 public GameObject testEnemyPrefab; 

Ele armazenará uma referência à testEnemyPrefab do Inimigo em testEnemyPrefab .

Para criar um inimigo ao executar o script, adicione o seguinte código ao Start() :

 Instantiate(testEnemyPrefab).GetComponent<MoveEnemy>().waypoints = waypoints; 

Portanto, criaremos uma nova cópia da pré-fabricada armazenada no testEnemy e atribuiremos uma rota a ela.

Salve o arquivo e retorne ao Unity. Selecione o objeto Estrada na Hierarquia e selecione a predefinição do Inimigo para o parâmetro Test Enemy .

Inicie o projeto e veja como o inimigo se move ao longo da estrada (no GIF, para maior clareza, a velocidade é aumentada em 20 vezes).


Percebeu que ele nem sempre olha para onde está indo? É engraçado, mas estamos tentando fazer um jogo profissional. Portanto, na segunda parte do tutorial, ensinaremos os inimigos a olhar para frente.

Para onde ir a seguir?


Já fizemos muito e estamos avançando rapidamente para criar nosso próprio jogo de defesa de torre.

Os jogadores podem criar um número limitado de monstros, e o inimigo corre ao longo da estrada, indo em direção ao nosso cookie. Os jogadores têm ouro e podem atualizar monstros.

Faça o download do resultado final aqui .

Na segunda parte, consideraremos a criação de enormes ondas de inimigos e sua destruição. Até breve!

Source: https://habr.com/ru/post/pt413837/


All Articles