[Unity Mini Game] Caso de desarrollo de juegos, ¡crea fácilmente un juego de defensa de torres! (Abajo)

Bienvenido a la segunda parte de cómo crear un juego de defensa de torres en Unity. Estás creando un juego de defensa de torres en Unity y, al final de la primera parte, puedes colocar y mejorar monstruos. También tienes un enemigo atacando la galleta.

¡Sin embargo, el enemigo no sabe hacia dónde mirar! Además, este fue un error crítico por parte del ataque. En esta parte, agregarás oleadas de generación de enemigos y armarás a tus monstruos para que puedan proteger tus preciadas galletas.

comenzar

En Unity, abra el proyecto que completó en la primera parte de esta serie de tutoriales o, si recién se está uniendo ahora, descargue el proyecto inicial y abra TowerDefense-Part2-Starter.

Haz girar al enemigo

Al final del último tutorial, los enemigos seguían el camino pero no parecían saber hacia dónde mirar.

Abra MoveEnemy.cs en el IDE y agregue el siguiente método para resolver este problema.

private void RotateIntoMoveDirection()
{
  
  Vector3 newStartPosition = waypoints [currentWaypoint].transform.position;
  Vector3 newEndPosition = waypoints [currentWaypoint + 1].transform.position;
  Vector3 newDirection = (newEndPosition - newStartPosition);
  
  float x = newDirection.x;
  float y = newDirection.y;
  float rotationAngle = Mathf.Atan2 (y, x) * 180 / Mathf.PI;
  
  GameObject sprite = gameObject.transform.Find("Sprite").gameObject;
  sprite.transform.rotation = Quaternion.AngleAxis(rotationAngle, Vector3.forward);
}

RotateIntoMoveDirectionrota al enemigo para que siempre mire hacia adelante, así:
RotateIntoMoveDirectionGira al enemigo para que siempre mire hacia adelante, así:

它通过从下一个航点的位置中减去当前航点的位置来计算 虫子 的当前移动方向。
它使用 `Mathf.Atan2` 来确定 `newDirection` 指向的角度(以弧度为单位),假设零点向右。将结果乘以 `180 / Mathf.PI` 会将角度转换为度数。
最后,它检索名为 Sprite 的子项,并沿 z 轴将其旋转 `rotationAngle` 度。请注意,您旋转的是子项而不是父项,因此生命条(您很快就会添加它)保持水平状态。

En Update(), // TODO: Rotate into move directionreemplace el comentario con RotateIntoMoveDirectionla siguiente llamada a :

RotateIntoMoveDirection();

Guarde el archivo y cambie a Unity. Dirige la escena; ahora tu monstruo sabe adónde va.

BugFollowsRoad.gif

¿Dónde aparece el error ahora?

¿Un enemigo? No es nada impresionante. Que venga la multitud. Como todos los juegos de defensa de torres, ¡las hordas vendrán en oleadas!

Notificar a los jugadores

Antes de enviar a la horda a la acción, debes informar a los jugadores sobre el ataque inminente. Además, ¿por qué no mostrar el número de la ola actual en la parte superior de la pantalla?

Varios objetos del juego necesitan generar información de onda, por lo que debes agregarla al componente GameManagerBehavior en GameManager.

Abra GameManagerBehavior en .cs en el IDE y luego agregue las dos variables siguientes:

public Text waveLabel;
public GameObject[] nextWaveLabels;

waveLabelAlmacena una referencia a la lectura de la onda en la esquina superior derecha de la pantalla. nextWaveLabelsAlmacena dos objetos del juego que, cuando se combinen, crearán una animación que mostrarás cuando comience una nueva ola, como esta:

siguienteOlaAnimación

Guarde el archivo y cambie a Unity. Seleccione Game Manager en la jerarquía. Haga clic en el círculo pequeño a la derecha de Generar etiquetas de onda y luego, en el cuadro de diálogo Seleccionar texto, seleccione Generar etiquetas de onda en la pestaña Escenarios.

Ahora establezca el tamaño de la siguiente ola de etiquetas en 2. Luego asigne el elemento 0 a NextWaveBottomLabel y el elemento 1 a NextWaveTopLabel de la misma manera que la etiqueta de onda.

Así debe ser el comportamiento de tu administrador de juegos

Si un jugador pierde el juego, no debería ver la siguiente ola de mensajes. Para solucionar este problema, vuelva a GameManagerBehavior.cs en el IDE y agregue otra variable:

public bool gameOver = false;

En gameOver, almacenarás si el jugador perdió el juego.

Del mismo modo, utilizarás propiedades para mantener los elementos del juego sincronizados con la ola actual. Agregue el siguiente código a 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);
  }
}

La creación de variables, propiedades y captadores privados ya debería ser algo natural. Pero nuevamente, el colocador es un poco complicado.

Utilice nuevas valueactualizaciones wave.

Luego compruebas si el juego no ha terminado. Si es así, recorre en iteración todas las etiquetas de nextWaveLabels (etiquetas que tienen componentes Animator). Para activar una animación en un animador, configure el disparador nextWave.

Finalmente, estableces waveLabelel valor de . ¿ Por qué ? – La gente normal no empieza a contar desde cero. Es raro, lo sé :]textwave + 1+1

En Start(), establezca el valor de esta propiedad:

Wave = 0;

Empiezas Wavea contar desde el número 0.

Guarde el archivo y ejecute la escena en Unity. Las lecturas de ondas comienzan correctamente en 1.

Internamente empiezas a contar con 0, pero para el jugador todo empieza con la onda 1.

Para los jugadores, todo comienza con la Ola 1.

Generar oleadas enemigas

Esto suena obvio, pero necesitas poder crear más enemigos para desatar hordas de enemigos; no puedes hacer eso ahora. Además, una vez que se borre la ola de generación actual, no deberías poder generar la siguiente ola, al menos no todavía.

Por lo tanto, el juego debe poder identificar si hay enemigos en la escena y las etiquetas son una buena forma de identificar los objetos del juego.

Establecer etiquetas enemigas

Seleccione la casa prefabricada enemiga en el navegador de proyectos. En la parte superior del inspector, haga clic en la lista desplegable Marcas y seleccione Agregar marca.

Crear etiqueta

Crea una etiqueta llamada enemigo_.
crear una nueva etiqueta

Selecciona la casa prefabricada enemiga. En el Inspector, establece su etiqueta como Enemigo.

Definir la información de la ola enemiga.

Ahora necesitas definir una ola de enemigos. Abra SpawnEnemy.cs en el IDE y SpawnEnemyagregue la siguiente implementación de clase antes:

[System.Serializable]
public class Wave
{
  public GameObject enemyPrefab;
  public float spawnInterval = 2;
  public int maxEnemies = 20;
}

Una oleada contiene enemyPrefab, que es la base para crear instancias de todos los enemigos en esa oleada, spawnIntervalel tiempo en segundos entre enemigos en la oleada y maxEnemies, la cantidad de enemigos generados en esa oleada.

Esta clase es serializable, lo que significa que puedes cambiar el valor en el inspector.

Agregue las siguientes variables a SpawnEnemyla clase:

public Wave[] waves;
public int timeBetweenWaves = 5;

private GameManagerBehavior gameManager;

private float lastSpawnTime;
private int enemiesSpawned = 0;

Esto configurará algunas variables para el desove que son muy similares a cómo mueves a los enemigos a lo largo de los puntos de ruta.
Definirás waveslas distintas oleadas del juego y realizarás un seguimiento de la cantidad de enemigos generados y sus tiempos de aparición en enemiesSpawnedy, respectivamente.lastSpawnTime

Los jugadores necesitan descansar después de matar, así que timeBetweenWavesconfigúralo en 5 segundos.

Reemplace Start()el contenido de con el siguiente código.

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

Aquí lastSpawnTimeconfigura la hora actual, que será la hora en la que se iniciará el guión inmediatamente después de que se cargue la escena. Luego recupérelo de una manera familiar GameManagerBehavior.

Añade esto a Update():

int currentWave = gameManager.Wave;
if (currentWave < waves.Length)
{
  
  float timeInterval = Time.time - lastSpawnTime;
  float spawnInterval = waves[currentWave].spawnInterval;
  if (((enemiesSpawned == 0 && timeInterval > timeBetweenWaves) ||
       timeInterval > spawnInterval) && 
      enemiesSpawned < waves[currentWave].maxEnemies)
  {
    
    lastSpawnTime = Time.time;
    GameObject newEnemy = (GameObject)
        Instantiate(waves[currentWave].enemyPrefab);
    newEnemy.GetComponent<MoveEnemy>().waypoints = waypoints;
    enemiesSpawned++;
  }
  
  if (enemiesSpawned == waves[currentWave].maxEnemies &&
      GameObject.FindGameObjectWithTag("Enemy") == null)
  {
    gameManager.Wave++;
    gameManager.Gold = Mathf.RoundToInt(gameManager.Gold * 1.1f);
    enemiesSpawned = 0;
    lastSpawnTime = Time.time;
  }
  
}
else
{
  gameManager.gameOver = true;
  GameObject gameOverText = GameObject.FindGameObjectWithTag ("GameWon");
  gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
}

Lea este código:

  • Obtenga el índice de la ola actual y verifique si es la última ola.
  • Si es así, calcula cuánto tiempo ha pasado desde que apareció el último enemigo y si es hora de generar un enemigo. Aquí se consideran dos escenarios. Si es el primer enemigo de la ola, comprueba timeIntervalsi es mayor que timeBetweenWaves. De lo contrario, verifique timeIntervalsi es más grande que esta ola spawnInterval. En cualquier caso, debes asegurarte de no generar todos los enemigos para esta ola.
  • enemyPrefabSi es necesario, genera enemigos a partir de copias instanciadas de . También puedes aumentar enemiesSpawnedel conteo.
  • Compruebas el número de enemigos en la pantalla. Si no, es el último enemigo de la oleada y generas la siguiente oleada. También le das al jugador el 10% del oro restante al final de la ola.
  • Después de superar la última ola de carreras, el juego gana la animación.

Establecer intervalo de generación

Guarde el archivo y cambie a Unity. Seleccione una carretera en la jerarquía. En el inspector, establece el tamaño de las ondas en 4.

Ahora configura el "Enemy Prefab" para los cuatro elementos en "Enemy". Establezca el intervalo de generación y el número máximo de campos de enemigos de la siguiente manera:

  • Elemento 0 : Intervalo de generación: 2,5 , enemigos máximos: 5
  • Elemento 1 : Intervalo de aparición: 2 , Enemigos máximos: 10
  • Elemento 2 : Intervalo de aparición: 2 , Enemigos máximos: 15
  • Elemento 3 : Intervalo de aparición: 1 , Enemigos máximos: 5

La configuración final debería verse como la captura de pantalla a continuación.

Ondas
Por supuesto, puedes usar estas configuraciones para aumentar o disminuir el daño de ataque.
ejecutar el juego. ¡Ajá! ¡Los insectos se dirigen a tus galletas!

errores.gif

Opcional: agrega diferentes tipos de enemigos.

Ningún juego de defensa de torres está completo con un solo tipo de enemigo. Afortunadamente, la carpeta prefabs contiene otra opción, Enemy2.

Seleccione el prefab\Enemy2 en el inspector y agréguele el script MoveEnemy. Establece su velocidad en 3 y su etiqueta en enemigo. ¡Ahora puedes usar este error rápido para mantener a los jugadores alerta!

Actualizar la salud del jugador

Incluso si enjambres de insectos se precipitan hacia la galleta, el jugador no sufrirá ningún daño. Pero eso es todo. Cuando el jugador deja que un enemigo invada, debe recibir un golpe.

Abra "Game Manager Behavior" en .cs en el IDE y luego agregue las dos variables siguientes:

public Text healthLabel;
public GameObject[] healthIndicator;

Lo usarás para healthLabelacceder a la lectura de salud del jugador y para acceder healthIndicatora los cinco pequeños monstruos crujientes de galletas verdes; simplemente representan la salud del jugador de una manera más interesante que las etiquetas de salud estándar.

Gestionar la salud

A continuación, agregue una propiedad para GameManagerBehaviormantener la salud del jugador en:

private int health;
public int Health
{
  get
  {
    return health;
  }
  set
  {
    
    if (value < health)
    {
      Camera.main.GetComponent<CameraShake>().Shake();
    }
    
    health = value;
    healthLabel.text = "HEALTH: " + health;
    
    if (health <= 0 && !gameOver)
    {
      gameOver = true;
      GameObject gameOverText = GameObject.FindGameObjectWithTag("GameOver");
      gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
    }
    
    for (int i = 0; i < healthIndicator.Length; i++)
    {
      if (i < Health)
      {
        healthIndicator[i].SetActive(true);
      }
      else
      {
        healthIndicator[i].SetActive(false);
      }
    }
  }
}

Esto gestionará la salud del jugador. Una vez más, la mayor parte del código está en los configuradores:

  • Si desea reducir la salud del jugador, use CameraShakeel componente para crear un agradable efecto de sacudida. Este script está incluido en el proyecto y no se trata aquí.
  • Actualice las variables privadas y las etiquetas de salud en la esquina superior izquierda de la pantalla.
  • Si la salud cae a 0 y el juego aún no ha terminado, gameOverconfigúralo truey activa GameOverla animación.
  • Elimina uno de los monstruos de la galleta. Sería más sencillo escribir este bit si simplemente los deshabilitara, pero también admite volver a habilitarlos al agregar salud.

Inicializar Healthen Start():

Health = 5;

Cuando la escena comience a reproducirse, Healthconfigúrelo en 5.

Después de configurar esta propiedad, ahora puede actualizar la salud del reproductor cuando un error llega a la cookie. Guarde este archivo y cambie a MoveEnemy.cs que aún está en el IDE.

Actualizar la visualización del valor de salud

Para actualizar la salud del jugador, busque el comentario Update()y // TODO: deduct healthreemplácelo con el siguiente código:

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

Esto tomará GameManagerBehaviory Healthle .

Guarde el archivo y cambie a Unity.

Seleccione Game Manager en la jerarquía y establezca su Etiqueta de salud en Etiqueta de salud.

Expanda Cookie en la jerarquía y arrastre y suelte sus cinco indicadores de salud secundarios en la matriz de indicadores de salud de GameManager: los indicadores de salud son pequeños monstruos verdes que comen galletas felizmente.

Reproduce la escena y espera a que el insecto llegue a la galleta. No hagas nada hasta que pierdas.

ataque de galletas

Monster Wars: La venganza del monstruo

¿Monstruo en su lugar? completo. ¿El enemigo avanza? completo. - ¡Parecen malos! ¡Es hora de cortar a esos tontos!

一个生命条,所以玩家知道哪些敌人是强的,哪些是弱的
探测怪物范围内的敌人
决策点——向哪个敌人开火

Barra de salud enemiga barra de salud enemiga

Implementarás la barra de salud usando dos imágenes, una para un fondo oscuro y otra para una barra verde un poco más pequeña que escalarás para que coincida con la salud del enemigo.

Arrastre Prefabs\Enemy desde el Explorador de proyectos a la escena.

Luego arrastre " Images\Objects\HealthBarBackground " a "Enemy" en la jerarquía, agregándolo como hijo.

En el Inspector, establezca la posición de HealthBarBackground en (0, 1, -4).

A continuación, seleccione " Imágenes\Objetos\HealthBar " en el Explorador de proyectos y asegúrese de que su "Perspectiva" esté configurada en "Izquierda". Luego, agrégalo como hijo del enemigo en la jerarquía y establece su posición en (-0,63, 1, -5). Establece su escala X en 125.

Agregue un nuevo script C# llamado HealthBar al objeto del juego HealthBar. Más adelante, lo editarás para ajustar la longitud de la barra de salud.

Después de seleccionar al enemigo en la jerarquía, asegúrese de que su posición sea (20, 0, 0).

Haga clic en Aplicar en la parte superior del inspector para guardar todos los cambios como parte de la casa prefabricada. Finalmente, elimina al enemigo de la jerarquía.

errores con la barra de salud

Ahora, repita estos pasos para agregar la barra de salud a la casa prefabricada _Prefabs\Enemy2_.

Ajustar la longitud de la barra de salud Ajustar la longitud de la barra de salud

Abra HealthBar.cs en el IDE y agregue las siguientes variables:

public float maxHealth = 100;
public float currentHealth = 100;
private float originalScale;

maxHealthAlmacena la salud máxima del enemigo y currentHealthrealiza un seguimiento de la salud restante. Finalmente, originalScalese recuerda el tamaño original de la barra de salud.

originalScaleGuarde el objeto Start()en:

originalScale = gameObject.transform.localScale.x;

Guarde el valor localScalede x.

Establezca la escala de la barra de salud agregando lo siguiente Update()a :

Vector3 tmpScale = gameObject.transform.localScale;
tmpScale.x = currentHealth / maxHealth * originalScale;
gameObject.transform.localScale = tmpScale;

Copie localScalea una variable temporal ya que no puede simplemente ajustar su valor x. Luego, calcule el nuevo x-tick en función del estado actual del error y restablezca la variable temporal localScale.

Guarda el archivo y ejecuta el juego en Unity. Verás una barra de salud encima del enemigo.

¡La resistencia es inútil!  - Espera, ¿qué resistencia?

Mientras se ejecuta el juego, expande un objeto enemigo (clon) en la jerarquía y selecciona su objeto secundario HealthBar. Cambie su valor de "Salud actual" y verifique la barra de salud para ver si desea cambiarlo.

Ajustar barra de salud

Seguimiento de enemigos dentro del alcance Seguimiento de enemigos dentro del alcance

Ahora los monstruos necesitan saber a qué enemigos apuntar. Antes de la implementación, tienes que hacer algo de trabajo de preparación con los monstruos y enemigos.

Seleccione Prefab\Monster en el Navegador de proyectos y agréguele el componente Circle Collider 2D en el Inspector.

Establece el radio del colisionador en 2,5; esto establecerá el alcance del monstruo.

Marca " Is Trigger " para que el objeto pase por la zona en lugar de golpearla.

Finalmente, en la parte superior del inspector, configura la capa del monstruo para que ignore la emisión de rayos. Haga clic en Sí, cambiar subclaves en el cuadro de diálogo. Si no se ignora el raycasting, el colisionador reacciona a los eventos de clic. Esto es un problema porque los monstruos bloquean eventos que apuntan a espacios abiertos debajo de ellos.

Captura de pantalla 2015-06-05 a las 14.47.15

Para permitir que se detecten enemigos en el área de activación, debes agregarle un colisionador y un cuerpo rígido, ya que Unity solo envía el evento de activación si uno de los colisionadores tiene un cuerpo rígido adjunto.

En el Explorador de proyectos, seleccione Prefab\Enemy. Agregue un componente 2D de cuerpo rígido con el tipo de cuerpo establecido en Cinemático. Esto significa que el cuerpo no debería verse afectado por la física.

Agrega un colisionador circular 2D con un radio de 1. Repita estos pasos para Prefab\Enemy2

Los activadores ahora están configurados para que los monstruos detecten cuando los enemigos están dentro de su alcance.

Una cosa que debes preparar: un script que notifique a los monstruos cuando un enemigo es destruido para que no causen excepciones al continuar disparando.

Cree un nuevo script C# llamado EnemyDestructionDelegate y agréguelo a las casas prefabricadas Enemy y Enemy2.

Abra EnemyDestructionDelegate.cs en el IDE y agregue la siguiente declaración de delegado:

public delegate void EnemyDelegate (GameObject enemy);
public EnemyDelegate enemyDelegate;

Aquí crea uno delegate, que es un contenedor para una función que se puede pasar como una variable.

Nota: Utilice delegados cuando desee que un objeto del juego notifique proactivamente los cambios a otros objetos del juego. Para obtener más información sobre la delegación, consulte la documentación de Unity.

Agregue los siguientes métodos:

void OnDestroy()
{
  if (enemyDelegate != null)
  {
    enemyDelegate(gameObject);
  }
}

Unity llama automáticamente a este método después de destruir el objeto del juego y verifica si el delegado no lo es null. En este caso puedes gameObjectllamarlo como argumento. Esto permite que todos los oyentes registrados como delegados sepan que el enemigo ha sido destruido.

Guarde el archivo y regrese a Unity.

Dale a los monstruos una licencia para matar

Dale al monstruo un guión para matar.

Los monstruos ahora pueden detectar enemigos dentro de su alcance. Agregue un nuevo script C# a la casa prefabricada de Monster y asígnele el nombre ShootEnemies.

Abra ShootEnemies.cs en el IDE y agregue las siguientes usinginstrucciones para acceder a él Generics.

using System.Collections.Generic;

Agrega una variable para rastrear a todos los enemigos dentro del alcance:

public List<GameObject> enemiesInRange;

En enemiesInRange, almacenarás a todos los enemigos dentro del alcance.

Inicializar Start()campos en .

enemiesInRange = new List<GameObject>();

Inicialmente, no hay enemigos dentro del alcance, por lo que creas una lista vacía.

¡Completa enemiesInRangela lista! Agregue este código al script:

void OnEnemyDestroy(GameObject enemy)
{
  enemiesInRange.Remove (enemy);
}

void OnTriggerEnter2D (Collider2D other)
{

  if (other.gameObject.tag.Equals("Enemy"))
  {
    enemiesInRange.Add(other.gameObject);
    EnemyDestructionDelegate del =
        other.gameObject.GetComponent<EnemyDestructionDelegate>();
    del.enemyDelegate += OnEnemyDestroy;
  }
}

void OnTriggerExit2D (Collider2D other)
{
  if (other.gameObject.tag.Equals("Enemy"))
  {
    enemiesInRange.Remove(other.gameObject);
    EnemyDestructionDelegate del =
        other.gameObject.GetComponent<EnemyDestructionDelegate>();
    del.enemyDelegate -= OnEnemyDestroy;
  }
}

  • En OnEnemyDestroy, eliminas al enemigo enemiesInRangede . OnTriggerEnter2DInvocado cuando los enemigos aprietan el gatillo alrededor de tu monstruo .

  • Luego agregue enemigos a enemiesInRangela lista y OnEnemyDestroyagréguelos a EnemyDestructionDelegate. Esto asegura que se llame cuando el enemigo sea destruido OnEnemyDestroy. No querrás que los monstruos desperdicien munición en enemigos muertos ahora, ¿verdad?

  • En OnTriggerExit2D, eliminas al enemigo de la lista y cancelas el registro de tu representante. Ahora sabes qué enemigos están dentro del alcance.

  • Guarda el archivo y ejecuta el juego en Unity. Para probar si funciona, coloca un monstruo, selecciónalo y observa enemiesInRangelos cambios en la lista en el inspector.

Seleccione un objetivo Seleccione un objetivo

Los monstruos ahora saben qué enemigos están dentro de su alcance. Pero, ¿qué hacen cuando hay varios enemigos dentro de su alcance?

¡Por supuesto, atacan a la persona más cercana a la galleta!

Abra MoveEnemy.cs en el IDE y agregue el siguiente método nuevo para calcular:

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

Este código calcula la longitud del camino que el enemigo aún no ha avanzado. Se utiliza Distancepara calcular Vector3la distancia entre dos instancias.

Utilizarás este método más adelante para identificar objetivos a los que atacar. Sin embargo, tu monstruo está desarmado, así que ocúpate de eso primero.

Guarde el archivo y regrese a Unity para comenzar a configurar viñetas.

Dale balas a los monstruos, ¡muchas balas!

Arrastre y suelte Imagen/Objeto/Bullet1 desde el Explorador de proyectos en la escena. Establezca la posición z en -2; las posiciones x e y no importan porque se configuran cada vez que se crea una instancia de una nueva viñeta en tiempo de ejecución.

Agregue un nuevo script C# denominado BulletBehavior y agréguele las siguientes variables en el IDE:

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;

speedDeterminar la velocidad de vuelo de la bala; damagese explica por sí mismo.

targety startPositiondeterminar targetPositionla dirección de la bala.

distancey startTimerealiza un seguimiento de la posición actual de la bala. gameManagerRecompensa al jugador cuando aplasta a los enemigos.

Start()Asigne valores a estas variables en :

startTime = Time.time;
distance = Vector2.Distance (startPosition, targetPosition);
GameObject gm = GameObject.Find("GameManager");
gameManager = gm.GetComponent<GameManagerBehavior>();

Establezca startTimela hora actual y calcule la distancia entre las posiciones inicial y objetivo. También puedes conseguirlo como de costumbre GameManagerBehavior.

Agregue el siguiente código Update()para controlar el movimiento de la bala:

float timeInterval = Time.time - startTime;
gameObject.transform.position = Vector3.Lerp(startPosition, targetPosition, timeInterval * speed / distance);


if (gameObject.transform.position.Equals(targetPosition))
{
  if (target != null)
  {
    
    Transform healthBarTransform = target.transform.Find("HealthBar");
    HealthBar healthBar = 
        healthBarTransform.gameObject.GetComponent<HealthBar>();
    healthBar.currentHealth -= Mathf.Max(damage, 0);
    
    if (healthBar.currentHealth <= 0)
    {
      Destroy(target);
      AudioSource audioSource = target.GetComponent<AudioSource>();
      AudioSource.PlayClipAtPoint(audioSource.clip, transform.position);

      gameManager.Gold += 50;
    }
  }
  Destroy(gameObject);
}

您可以使用 `Vector3.Lerp` 计算新的项目符号位置,以在开始位置和结束位置之间进行插值。
如果项目符号到达 `targetPosition` ,则验证 `target` 是否仍然存在。
检索目标的 `HealthBar` 组件,并通过项目符号的 `damage` 降低其生命值。
如果敌人的生命值降至零,您可以摧毁它,播放声音效果并奖励玩家的枪法。

Guarde el archivo y regrese a Unity.

Consigue balas más grandes Consigue balas más grandes

¿No sería genial si tus monstruos dispararan balas más grandes en niveles más altos? - ¡Sí, sí, lo será! Afortunadamente, esto es fácil de lograr.

Arrastre y suelte el objeto del juego Bullet1 desde la Jerarquía a la pestaña Proyecto para crear la casa prefabricada de la viñeta. Elimina el objeto original de la escena; ya no lo necesitarás.

Duplica la casa prefabricada Bullet1 dos veces. Nombra las copias Bullet2 y Bullet3.

Seleccione la viñeta 2. En el Inspector, establezca el campo Sprite del componente Sprite Renderer en Imagen/Objeto/Bullet2. Esto hace que Bullet2 parezca un poco más grande que Bullet1.

Repita el proceso, configurando el objeto prefabricado Bullet3 en Imagen/Objeto/Bullet3.

A continuación, establezca el daño causado por la bala en el comportamiento de la bala.

Seleccione la casa prefabricada Bullet1 en la pestaña Proyecto. En el inspector puedes ver el comportamiento de la bala (script), donde estableces el daño de la bala 1 a 10, el daño de la bala 2 a 15 y el daño de la bala 3 a 20, o lo que te haga feliz.

Nota: Configuré los valores para que a niveles más altos, el costo por daño sea mayor. Esto compensa el hecho de que las actualizaciones permiten a los jugadores mejorar los monstruos en ubicaciones óptimas.

Prefabricados tipo bala: el tamaño aumenta con el nivel

Casa prefabricada tipo bala: el tamaño aumenta con los niveles

Nivelando las balas Nivelando las balas

Asigna diferentes balas a diferentes niveles de monstruos para que los monstruos más fuertes destruyan a los enemigos más rápido.

Abra MonsterData en .cs en el IDE y agregue estas variables a MonsterLevel:

public GameObject bullet;
public float fireRate;

Estos establecerán las prefabricaciones de balas y las velocidades de disparo para cada nivel de monstruo. Guarde el archivo y regrese a Unity para completar la configuración del monstruo.

Seleccione la casa prefabricada monstruo en el navegador de proyectos. En el Inspector, expanda el nivel en el componente Monster Data (Script). Establece la "velocidad de disparo" de cada elemento en 1. Luego, establezca las viñetas de los elementos 0, 1 y 2 en la viñeta 1, la viñeta 2 y la viñeta 3 respectivamente.

Tus niveles de monstruos deben configurarse de la siguiente manera:

MonsterData con balas

¿Las balas matan a tus enemigos? -¡examinar! ¡Fuego!

abran fuego

Abra ShootEnemies.cs en el IDE y agregue algunas variables:

private float lastShotTime;
private MonsterData monsterData;

Como sugiere el nombre, estas variables rastrean la última vez que este monstruo disparó, así como MonsterDatala estructura, que incluye información sobre el tipo de bala, la velocidad de disparo, etc.

Asigne valores a Start()estos campos en:

lastShotTime = Time.time;
monsterData = gameObject.GetComponentInChildren<MonsterData>();

Aquí lastShotTimeconfigura la hora actual y accede MonsterDataal componente de este objeto.

Agregue el siguiente método para lograr el disparo:

void Shoot(Collider2D target)
{
  GameObject bulletPrefab = monsterData.CurrentLevel.bullet;
  
  Vector3 startPosition = gameObject.transform.position;
  Vector3 targetPosition = target.transform.position;
  startPosition.z = bulletPrefab.transform.position.z;
  targetPosition.z = bulletPrefab.transform.position.z;

  
  GameObject newBullet = (GameObject)Instantiate (bulletPrefab);
  newBullet.transform.position = startPosition;
  BulletBehavior bulletComp = newBullet.GetComponent<BulletBehavior>();
  bulletComp.target = target.gameObject;
  bulletComp.startPosition = startPosition;
  bulletComp.targetPosition = targetPosition;

  
  Animator animator = 
      monsterData.CurrentLevel.visualization.GetComponent<Animator>();
  animator.SetTrigger("fireShot");
  AudioSource audioSource = gameObject.GetComponent<AudioSource>();
  audioSource.PlayOneShot(audioSource.clip);
}

  • Obtenga la posición inicial de la bala y la posición objetivo. Establezca la posición z en bulletPrefabla posición de . Anteriormente, configuraste el valor de posición z de la bala prefabricada para asegurar que la bala aparezca detrás del monstruo que la disparó, pero frente al enemigo.
  • Crea una instancia de una nueva viñeta usando bulletPrefabrepresentación . MonsterLevelAsigne viñetas startPositiony targetPosition.
  • Haz que el juego sea más interesante: ejecuta animaciones de disparo y reproduce sonidos de láser cuando los monstruos disparan.

poner todo junto

Es hora de conectar todo junto. Identifica el objetivo y haz que tu monstruo lo mire.

Aún en ShootEnemies.cs, agregue este código a Update():

GameObject target = null;

float minimalEnemyDistance = float.MaxValue;
foreach (GameObject enemy in enemiesInRange)
{
  float distanceToGoal = enemy.GetComponent<MoveEnemy>().DistanceToGoal();
  if (distanceToGoal < minimalEnemyDistance)
  {
    target = enemy;
    minimalEnemyDistance = distanceToGoal;
  }
}

if (target != null)
{
  if (Time.time - lastShotTime > monsterData.CurrentLevel.fireRate)
  {
    Shoot(target.GetComponent<Collider2D>());
    lastShotTime = Time.time;
  }
  
  Vector3 direction = gameObject.transform.position - target.transform.position;
  gameObject.transform.rotation = Quaternion.AngleAxis(
      Mathf.Atan2 (direction.y, direction.x) * 180 / Mathf.PI,
      new Vector3 (0, 0, 1));
}

Lea este código.

  • Determina el objetivo del monstruo. Empiece desde la máxima distancia posible minimalEnemyDistanceen . Recorre todos los enemigos dentro del alcance y, si la distancia del enemigo a la galleta es menor que el mínimo actual, configúrala como el nuevo objetivo.
  • Si el tiempo transcurrido es mayor que la velocidad de disparo del monstruo, llámalo Shooty lastShotTimeconfigúralo en el tiempo actual.
  • Calcula el ángulo de rotación entre el monstruo y su objetivo. Estableces la rotación del monstruo en este ángulo. Ahora siempre mira hacia el objetivo.

Guarde el archivo y juegue en Unity. Tu monstruo protege vigorosamente tus galletas. ¡Ya terminaste totalmente, totalmente!

fin

Vaya, realmente hiciste mucho entre los dos tutoriales y tienes un juego genial para mostrarlo.
Aquí hay algunas ideas para aprovechar el trabajo que ha realizado:

  • Más tipos de enemigos y monstruos
  • Múltiples caminos enemigos
  • diferentes niveles de enemigos

El blogger es un jugador autodidacta. Si también eres principiante de Unity, bienvenido a unirte a mi chat grupal para ayuda y comunicación mutua: 618012892

Supongo que te gusta

Origin blog.csdn.net/weixin_72715182/article/details/130629834
Recomendado
Clasificación