Hacer de Tower Defense un juego de unidad - Parte 1

imagen

Los juegos de defensa de la torre están ganando popularidad, y esto no es sorprendente: ¡poco se puede comparar con el placer de observar sus propias líneas de defensa que destruyen a los malvados enemigos! ¡En este tutorial de dos partes, crearemos un juego de torre de defensa en el motor de Unity !

Aprenderá a hacer lo siguiente:

  • Crea oleadas de enemigos
  • Haz que sigan los puntos de ruta
  • Construye y mejora torres, y también enséñales cómo dividir a los enemigos en píxeles pequeños

Al final, obtenemos el marco del juego, que puede desarrollarse aún más.

Nota : necesita conocimientos básicos de Unity (por ejemplo, necesita saber cómo se agregan los activos y componentes, qué son los prefabricados) y los conceptos básicos de C # . Para aprender todo esto, le recomiendo que lea los tutoriales sobre Unity de Sean Duffy o la serie Beginning C # with Unity de Brian Mockley.

Trabajaré en Unity para OS X, pero este tutorial también es adecuado para Windows.

A través de las ventanas de la torre de marfil


En este tutorial, crearemos un juego de defensa de la torre en el que los enemigos (pequeños insectos) se arrastran hacia una galleta que te pertenece a ti y a tus secuaces (por supuesto, ¡estos son monstruos!). El jugador puede colocar monstruos en puntos estratégicos y mejorarlos para obtener oro.

El jugador debe matar a todos los errores hasta que lleguen a la cookie. Cada nueva ola de enemigos se está volviendo cada vez más difícil de vencer. El juego termina cuando sobrevives a todas las olas (¡victoria!) O cuando cinco enemigos se arrastran hacia las galletas (¡pérdida!).

Aquí hay una captura de pantalla del juego terminado:


¡Monstruos, uníos! ¡Protege la galleta!

Llegar al trabajo


Descargue este proyecto en blanco , descomprímalo y abra el proyecto TowerDefense-Part1-Starter en Unity.

El proyecto borrador tiene recursos de gráficos y sonidos, animaciones listas para usar y varios scripts útiles. Los guiones no están directamente relacionados con los juegos de defensa de la torre, por lo que no hablaré de ellos aquí. Sin embargo, si desea obtener más información sobre la creación de animaciones 2D en Unity, consulte este tutorial de Unity 2D .

El proyecto también contiene prefabricados, que agregaremos más adelante para crear personajes. Finalmente, hay una escena en el proyecto con un fondo y una interfaz de usuario personalizada.

Abra el GameScene ubicado en la carpeta Escenas y configure el modo Juego en una relación de aspecto de 4: 3 para que todas las etiquetas coincidan con el fondo correctamente. En el modo Juego, verá lo siguiente:


Autoría:

  • ¡Los gráficos para el proyecto están tomados del paquete gratuito Wiki Wenderlich! Otros trabajos gráficos se pueden encontrar en su sitio web gameartguppy .
  • ¡ Buena música tomada de BenSound , que tiene otras bandas sonoras increíbles!
  • También agradezco a Michael Jesper por la útil función de movimiento de la cámara.
.

El lugar está marcado con una cruz: la ubicación de los monstruos.


Los monstruos solo se pueden colocar en puntos marcados con una x .

Para agregarlos a la escena, arrastre Imágenes \ Objetos \ Openspot desde el Navegador de proyectos a la ventana Escena . Si bien la posición no es importante para nosotros.

Una vez que haya seleccionado Openspot en la jerarquía, haga clic en Agregar componente en el Inspector y seleccione Box Collider 2D . En la ventana Escena, Unity mostrará un colisionador rectangular con una línea verde. Utilizaremos este colisionador para reconocer los clics del mouse en esta ubicación.


Agregue el componente Audio \ Audio Source a Openspot de la misma manera. Para el parámetro AudioClip del componente Fuente de audio, seleccione el archivo tower_place ubicado en la carpeta Audio y desactive Play On Awake .

Necesitamos crear 11 puntos más. Aunque existe la tentación de repetir todos estos pasos, Unity tiene una mejor solución: ¡ Prefab !

Arrastre Openspot desde la Jerarquía a la carpeta Prefabs dentro del Navegador de proyectos . Su nombre se volverá azul en la Jerarquía, lo que significa que está adjunto al prefabricado. Algo como esto:


Ahora que tenemos el espacio prefabricado en blanco, podemos crear tantas copias como queramos. Simplemente arrastre y suelte Openspot desde la carpeta Prefabs dentro del Navegador de proyectos en la ventana Escena . Repita esto 11 veces y aparecerán 12 objetos Openspot en la escena.

Ahora use el Inspector para configurar estos 12 objetos Openspot con las siguientes 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)

Cuando haces esto, la escena se verá así:


Colocamos monstruos


Para simplificar la colocación, hay una prefabricada de Monster en la carpeta Prefab del proyecto.


Monster Prefab listo para usar

Por el momento, consiste en un objeto de juego vacío con tres sprites diferentes y animaciones de disparos cuando eran niños.

Cada sprite es un monstruo con diferentes niveles de poder. El prefabricado también contiene el componente Fuente de audio , que se lanzará para reproducir sonido cuando un monstruo dispara un láser.

Ahora crearemos un script que alojará a Monster en Openspot .

En el Navegador de proyectos, seleccione el objeto Openspot en la carpeta Prefabs . En el Inspector, haga clic en Agregar componente y luego seleccione Nuevo guión y asigne un nombre al guión PlaceMonster . Seleccione C Sharp como el idioma y haga clic en Crear y Agregar . Como agregamos el script al prefabricado de Openspot , todos los objetos de Openspot en la escena ahora tendrán este script. Genial

Haga doble clic en el script para abrirlo en el IDE. Luego agregue dos variables:

public GameObject monsterPrefab; private GameObject monster; 

Crearemos una instancia del objeto almacenado en monsterPrefab para crear el monstruo y lo almacenaremos en un monster para que pueda manipularse durante el juego.

Un monstruo por punto


Para que solo se pueda poner un monstruo en un punto, agregue el siguiente método:

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

En CanPlaceMonster() podemos verificar si la variable monster sigue siendo null . Si es así, entonces no hay ningún monstruo en el punto, y podemos ubicarlo.

Ahora agregue el siguiente código para colocar el monstruo cuando el jugador haga clic en este 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 al monstruo cuando haces clic con el mouse o tocas la pantalla. Como trabaja el

  1. Unity llama automáticamente a OnMouseUp cuando un jugador toca el colisionador físico GameObject.
  2. Cuando se llama, este método pone un monstruo si CanPlaceMonster() devuelve true .
  3. Creamos un monstruo usando el método Instantiate , que crea una instancia del prefabricado dado con la posición y rotación especificadas. En este caso, copiamos monsterPrefab , le damos la posición actual de GameObject y no giramos, transferimos el resultado a GameObject y lo GameObject en monster
  4. Al final, llamamos a PlayOneShot para reproducir el efecto de sonido adjunto al componente AudioSource del objeto.

Ahora nuestro script PlaceMonster puede tener un nuevo monstruo, pero aún necesitamos especificar un prefabricado.

Usando el prefabricado correcto


Guarde el archivo y regrese a Unity.

Para configurar la variable monsterPrefab , primero seleccione el objeto Openspot de la carpeta Prefabs en el navegador del proyecto.

En el Inspector, haga clic en el círculo a la derecha del campo Monster Prefab del componente PlaceMonster (Script) y seleccione Monster en el cuadro de diálogo que aparece.


Eso es todo Inicie la escena y cree monstruos en diferentes lugares haciendo clic con el mouse o tocando la pantalla.


Genial Ahora podemos crear monstruos. Sin embargo, se ven como un desastre extraño, porque todos los niños sprites del monstruo están dibujados. Ahora lo arreglaremos.

Elevar el nivel de monstruos


La siguiente figura muestra que con un aumento en el nivel, los monstruos se ven cada vez más aterradores.


¡Qué linda! Pero si intentas robarle las galletas, este monstruo se convertirá en un asesino.

El script se utiliza como base para la implementación del sistema de niveles de monstruos. Rastrea el poder del monstruo en cada nivel y, por supuesto, el nivel actual del monstruo.

Agrega este script.

Seleccione Prefabs / Monster prefab en Project Browser . Agregue un nuevo script de C # llamado MonsterData . Abra el script en el IDE y agregue el siguiente código sobre la clase MonsterData .

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

Entonces creamos MonsterLevel . Agrupa el precio (en oro, que apoyaremos a continuación) y una representación visual del nivel del monstruo.

Agregamos encima de [System.Serializable] para que las instancias de clase se puedan modificar en el inspector. Esto nos permite cambiar rápidamente todos los valores de la clase Level, incluso cuando el juego se está ejecutando. Esto es increíblemente útil para equilibrar el juego.

Establecer niveles de monstruos


En nuestro caso, almacenaremos el MonsterLevel especificado en la List<T> .

¿Por qué no usar MonsterLevel[] ? Necesitaremos el índice de un objeto MonsterLevel específico varias veces. Aunque es fácil escribir código para esto, todavía tenemos que usar IndexOf() , que implementa la funcionalidad Lists . No tiene sentido reinventar la rueda.


Reinventar la bicicleta suele ser una mala idea.

En la parte superior de MonsterData.cs, agregue lo siguiente using construct:

 using System.Collections.Generic; 

Nos da acceso a estructuras de datos generalizadas para que podamos usar la clase List<T> en el script.

Nota : las generalizaciones son un poderoso concepto de C #. Le permiten especificar estructuras de datos de tipo seguro sin tener que adherirse al tipo. Esto es útil para clases de contenedor como listas y conjuntos. Para obtener más información sobre las estructuras genéricas, lea el libro Introducción a los genéricos de C # .

Ahora agregue la siguiente variable a MonsterData para mantener la lista MonsterLevel :

 public List<MonsterLevel> levels; 

Gracias a las generalizaciones, podemos garantizar que la List del level solo contendrá objetos MonsterLevel .

Guarde el archivo y cambie a Unity para configurar cada nivel.

Seleccione Prefabs / Monster en Project Browser . El Inspector ahora muestra el campo Niveles del componente MonsterData (Script) . Establezca el tamaño en 3 .


A continuación, establezca el costo para cada nivel:

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

Ahora asignamos los valores de los campos de visualización.

Expanda Prefabs / Monster en el navegador de proyectos para ver sus elementos secundarios . Arrastre el niño Monster0 al campo Elemento de visualización 0 .

A continuación, establezca el Elemento 1 en Monster1 y el Elemento 2 en Monster2 . El GIF muestra este proceso:


Cuando selecciona Prefabs / Monster , el prefab debería verse así:


Establecer nivel actual


Regrese a MonsterData.cs en el IDE y agregue otra variable a MonsterData .

 private MonsterLevel currentLevel; 

En la variable privada currentLevel almacenaremos el nivel actual del monstruo.

Ahora configure currentLevel y currentLevel visible para otros scripts. Agregue las siguientes líneas a MonsterData junto con la declaración de variables de instancia:

 //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); } } } } } 

Bastante gran parte del código C #, ¿verdad? Tomémoslo en orden:

  1. Establezca la propiedad de la variable privada currentLevel . Al establecer la propiedad, podemos llamarla como cualquier otra variable: ya sea como CurrentLevel (dentro de la clase) o como monster.CurrentLevel (fuera). Podemos definir cualquier comportamiento en el método getter o setter de una propiedad, y al crear solo un getter, setter o ambos, puede controlar las propiedades de la propiedad: solo lectura, solo escritura y escritura / lectura.
  2. En el captador, devolvemos el valor de currentLevel .
  3. En el setter, asignamos a currentLevel nuevo valor. Luego obtenemos el índice del nivel actual. Finalmente, recorremos todos los niveles y habilitamos / deshabilitamos la visualización visual dependiendo de currentLevelIndex . Esto es genial porque cuando currentLevel cambia, el sprite se actualiza automáticamente. ¡Las propiedades son una cosa muy conveniente!

Agregue la siguiente implementación de OnEnable :

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

Aquí establecemos CurrentLevel al colocar. Esto asegura que solo se muestre el sprite deseado.

Nota : es importante inicializar la propiedad en OnEnable , y no en OnStart , porque llamamos a los métodos ordinales al crear instancias prefabricadas.

OnEnable se llamará inmediatamente cuando se OnEnable (si el prefab se guardó en el estado habilitado), pero no se llama a OnStart hasta que el objeto comience a ejecutarse como parte de la escena.

Necesitamos verificar estos datos antes de colocar el monstruo, por lo que lo inicializamos en OnEnable .

Guarde el archivo y regrese a Unity. Ejecute el proyecto y coloque los monstruos; ahora muestran los sprites correctos del nivel más bajo.


Actualización de monstruo


Regrese al IDE y agregue el siguiente método a MonsterData :

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

En GetNextLevel obtenemos el índice currentLevel y el índice de nivel más alto; Si el monstruo no ha alcanzado el nivel máximo, el siguiente nivel regresa. De lo contrario, null devuelve null .

Puede usar este método para averiguar si es posible una actualización monstruosa.

Para elevar el nivel del monstruo, agregue el siguiente método:

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

Aquí obtenemos el índice del nivel actual, y luego nos aseguramos de que este no sea el nivel máximo, verificando que sea inferior a los levels.Count - 1 . Si es así, CurrentLevel al siguiente nivel.

Comprobación de la funcionalidad de actualización


Guarde el archivo y regrese a PlaceMonster.cs en el IDE. Agrega un nuevo método:

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

Primero verificamos si hay un monstruo que pueda mejorarse comparando la variable del monster con null . Si esto es cierto, obtenemos el nivel de monstruo actual de su MonsterData .

Luego verificamos si el siguiente nivel está disponible, es decir, si GetNextLevel() no devuelve null . Si es posible un aumento de nivel, entonces devolvemos true ; de lo contrario, devuelve false .

Implementamos mejoras para el oro.


Para habilitar la opción de actualización, agregue el else if OnMouseUp a OnMouseUp :

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

Verificamos la posibilidad de una actualización usando CanUpgradeMonster() . Si es posible, accedemos al componente MonsterData usando GetComponent() y llamamos a IncreaseLevel() , que aumenta el nivel del monstruo. Finalmente, lanzamos Monster AudioSource .

Guarde el archivo y regrese a Unity. Ejecuta el juego, coloca y actualiza cualquier cantidad de monstruos (pero por ahora).


Paying Gold - Game Manager


Si bien podemos construir y mejorar de inmediato cualquier monstruo, ¿será interesante en el juego?

Veamos el tema del oro. El problema con el seguimiento es que tenemos que transferir información entre diferentes objetos del juego.

La siguiente figura muestra todos los objetos que deberían participar en esto.


Todos los objetos de juego seleccionados deben saber cuánto oro tiene un jugador.

Para almacenar estos datos, utilizaremos un objeto común al que pueden acceder otros objetos.

Haga clic derecho en la Jerarquía y seleccione Crear vacío . Nombra el nuevo objeto GameManager .

Agregue un nuevo script C # llamado GameManagerBehavior a GameManager , y luego ábralo en el IDE. Mostraremos la cantidad total de oro del jugador en la etiqueta, por lo que en la parte superior del archivo agregue la siguiente línea:

 using UnityEngine.UI; 

Esto nos permitirá acceder a clases de IU como Text , que se usa para etiquetas. Ahora agregue la siguiente variable a la clase:

 public Text goldLabel; 

Almacenará un enlace al componente de Text utilizado para mostrar la cantidad de oro que tiene un jugador.

Ahora que GameManager conoce la etiqueta, ¿cómo sincronizamos la cantidad de oro almacenada en la variable y el valor que se muestra en la etiqueta? Crearemos una propiedad.

Agregue el siguiente código a GameManagerBehavior :

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

¿Te parece familiar? El código es similar a CurrentLevel , que configuramos en Monster . Primero creamos un gold variable privado para contener la cantidad actual de oro. Luego establecemos la propiedad Gold (inesperadamente, ¿verdad?) E implementamos el getter y setter.

El captador simplemente devuelve el valor del gold . El setter es más interesante. Además de establecer el valor de la variable, también establece el campo de text para que goldLabel muestre el nuevo valor de oro.

¿Cuán generosos seremos? Agregue la siguiente línea a Start() para darle al jugador 1000 de oro, o menos si siente pena por el dinero:

 Gold = 1000; 

Asignación de un objeto de etiqueta a un script


Guarde el archivo y regrese a Unity. En la Jerarquía, seleccione GameManager . En el Inspector, haga clic en el círculo a la derecha de la etiqueta dorada . En el cuadro de diálogo Seleccionar texto , seleccione la pestaña Escena y seleccione GoldLabel .


Ejecute la escena y la etiqueta mostrará Gold: 1000 .


Comprobando la "billetera" del jugador


Abra el script PlaceMonster.cs en el IDE y agregue la siguiente variable de instancia:

 private GameManagerBehavior gameManager; 

Utilizaremos gameManager para acceder al componente GameManagerBehavior objeto GameManagerBehavior en la escena. Para especificarlo, agregue lo siguiente a Start() :

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

Obtenemos un GameObject llamado GameManager usando la función GameObject.Find() , que devuelve el primer objeto del juego encontrado con ese nombre. Luego obtenemos su componente GameManagerBehavior y lo GameManagerBehavior para el futuro.

Nota : puede hacer esto configurando un campo en el editor de Unity o agregando un método estático a GameManager que devuelva una instancia del singleton del que podemos obtener GameManagerBehavior .

Sin embargo, en el bloque de código que se muestra arriba hay un caballo oscuro: el método Find , que funciona más lentamente durante la ejecución de la aplicación; pero es conveniente y se puede usar con moderación.

Toma mi dinero!


Todavía no hemos restado el oro, por lo que agregaremos esta línea dos veces a OnMouseUp() , reemplazando cada uno de los comentarios // TODO: :

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

Guarde el archivo y regrese a Unity, actualice algunos monstruos y mire la actualización del valor Gold. Ahora deducimos oro, pero los jugadores pueden construir monstruos siempre que tengan suficiente espacio; solo piden prestado dinero.


Crédito infinito? Genial Pero no podemos permitirlo. El jugador debe poder apostar monstruos mientras tenga suficiente oro.

Cheque de oro para monstruos


Cambie el IDE a PlaceMonster.cs y reemplace el contenido de CanPlaceMonster() siguiente:

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

Obtenemos MonsterData precio de colocación de monstruos de los levels en su MonsterData . Luego verificamos que el monster no monster null , y que gameManager.Gold más que este precio.

La tarea para usted: agregar independientemente a CanUpgradeMonster() cheque si el jugador tiene suficiente oro.

Solución en el interior
Reemplace la línea:

 return true; 

en esto:

 return gameManager.Gold >= nextLevel.cost; 

Verificará si el jugador tiene más Oro que el precio de actualización.

Guarda y ejecuta la escena en Unity. ¡Ahora prueba cómo agregar monstruos ilimitadamente!


Ahora solo podemos construir un número limitado de monstruos.

Política de la torre: enemigos, olas y puntos de referencia


Es hora de "allanar el camino" a nuestros enemigos. Los enemigos aparecen en el primer punto de la ruta, se mueven al siguiente y repiten el proceso hasta que alcanzan la cookie.

Puedes hacer que los enemigos se muevan así:

  1. Establece el camino que seguirán los enemigos
  2. Mueve al enemigo por el camino
  3. Gira al enemigo para que mire hacia adelante

Creando un camino desde waypoints


Haga clic derecho en la Jerarquía y seleccione Crear vacío para crear un nuevo objeto de juego vacío. Nómbrelo Road y colóquelo en (0, 0, 0) .

Ahora haz clic derecho en Road en la Jerarquía y crea otro objeto de juego vacío como hijo de Road. Nómbralo Waypoint0 y colócalo en el punto (-12, 2, 0) : desde aquí, los enemigos comenzarán su movimiento.


Del mismo modo, cree cinco puntos de ruta más con los siguientes nombres y posiciones:

  • Waypoint1: (X: 7, Y: 2, Z: 0)
  • Waypoint2: (X: 7, Y: -1, Z: 0)
  • Waypoint3: (X: -7.3, Y: -1, Z: 0)
  • Waypoint4: (X: -7.3, Y: -4.5, Z: 0)
  • Waypoint5: (X: 7, Y: -4.5, Z: 0)

La captura de pantalla siguiente muestra los puntos de ruta y la ruta resultante.


Haciendo enemigos


Ahora crea algunos enemigos para que puedan moverse por el camino. Hay un prefab Enemigo en la carpeta Prefabs . Su posición es (-20, 0, 0) , por lo que se crearán nuevas instancias fuera de la pantalla.

En todos los demás aspectos, está configurado casi de la misma manera que el monstruo prefabricado, tiene AudioSource y un niño Sprite , y podemos rotar este sprite en el futuro sin girar la barra de salud.


Movemos enemigos por el camino


Agregue un nuevo script de C # llamado MoveEnemy al prefabricado Prefabs \ Enemy . Abra el script en el IDE y agregue las siguientes variables:

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

En los waypoints , una copia de los waypoints se almacena en la matriz, y la línea [HideIn inspector ] encima de los waypoints asegura que no podemos cambiar accidentalmente este campo en el Inspector , pero aún tendremos acceso a él desde otros scripts.

currentWaypoint realiza un seguimiento de dónde proviene la ruta del enemigo en el momento actual, y lastWaypointSwitchTime almacena el tiempo que el enemigo pasó por ella. Además, almacenamos la speed enemigo.

Agregue esta línea a Start() :

 lastWaypointSwitchTime = Time.time; 

Entonces inicializamos lastWaypointSwitchTime con el valor de la hora actual.

Para que el enemigo se mueva a lo largo de la ruta, agrega el siguiente 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:   } } 

Analicemos el código paso a paso:

  1. Del conjunto de puntos de ruta obtenemos las posiciones de inicio y finalización del segmento de ruta actual.
  2. Calculamos el tiempo requerido para cubrir toda la distancia usando la fórmula tiempo = distancia / velocidad , y luego determinamos el tiempo actual en la ruta. Usando Vector2.Lerp , interpolamos la posición actual del enemigo entre el segmento exacto inicial y final.
  3. Comprueba si el enemigo ha llegado al endPosition . En caso afirmativo, procesamos dos escenarios posibles:
    1. El enemigo aún no ha alcanzado el último punto de la ruta, así que aumente el valor de currentWaypoint y actualice lastWaypointSwitchTime . Más tarde agregaremos un código para convertir al enemigo para que mire en la dirección de su movimiento.
    2. El enemigo ha llegado al último punto de la ruta, luego lo destruimos y comenzamos el efecto de sonido. Más adelante agregaremos un código que reduce la health del jugador.

Guarde el archivo y regrese a Unity.

Informamos a los enemigos de la dirección del movimiento.


En su estado actual, los enemigos no conocen el orden de los puntos de ruta.

Seleccione Road en la Jerarquía y agregue un nuevo script de C # llamado SpawnEnemy . Ábralo en el IDE y agregue la siguiente variable:

 public GameObject[] waypoints; 

Utilizaremos waypoints para almacenar referencias al waypoint en la escena en el orden deseado.

Guarde el archivo y regrese a Unity. Seleccione Carretera en la Jerarquía y establezca el Tamaño de la matriz de Waypoints en 6 .

Arrastre cada uno de los elementos secundarios de Road a los campos pegando Waypoint0 en el Elemento 0 , Waypoint1 en el Elemento 1, y así sucesivamente.


Ahora tenemos una matriz que contiene los puntos de ruta en el orden correcto: ten en cuenta que los enemigos nunca retroceden, se esfuerzan persistentemente por una dulce recompensa.

Mira cómo funciona todo


Abra SpawnEnemy en el IDE y agregue la siguiente variable:

 public GameObject testEnemyPrefab; 

Almacenará una referencia al testEnemyPrefab enemigo en testEnemyPrefab .

Para crear un enemigo al ejecutar el script, agregue el siguiente código a Start() :

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

Entonces crearemos una nueva copia del testEnemy almacenado en testEnemy y le asignaremos una ruta.

Guarde el archivo y regrese a Unity. Seleccione el objeto Road en la Jerarquía y seleccione el prefabricado Enemigo para el parámetro Test Enemy .

Inicie el proyecto y vea cómo se mueve el enemigo a lo largo del camino (en GIF, para mayor claridad, la velocidad aumenta en 20 veces).


¿Notó que no siempre mira a dónde va? Es divertido, pero estamos tratando de hacer un juego profesional. Por lo tanto, en la segunda parte del tutorial, enseñaremos a los enemigos a mirar hacia adelante.

¿A dónde ir después?


Ya hemos hecho mucho y estamos avanzando rápidamente hacia la creación de nuestro propio juego de defensa de la torre.

Los jugadores pueden crear un número limitado de monstruos, y el enemigo corre a lo largo del camino, en dirección a nuestra galleta. Los jugadores tienen oro y pueden actualizar monstruos.

Descargue el resultado final desde aquí .

En la segunda parte, consideraremos la creación de enormes oleadas de enemigos y su destrucción. Hasta pronto!

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


All Articles