Siguiendo el camino y controlando el tráfico
A veces necesitamos personajes de IA para recorrer el mundo del juego, siguiendo un camino más o menos definido o definido con precisión. Por ejemplo, en un juego de carreras, los oponentes de IA deben viajar a lo largo del camino, y en RTS, las unidades deben poder moverse al punto deseado, moverse a lo largo del terreno y teniendo en cuenta la posición de los demás.
Para parecer inteligentes, los agentes de IA deben poder determinar lo que están haciendo, y si no pueden llegar al punto deseado, deben ser capaces de trazar la ruta más efectiva y cambiar su camino cuando aparecen obstáculos en el camino.
Evitar obstáculos es un comportamiento simple que permite a las entidades de IA alcanzar los puntos objetivo. Es importante tener en cuenta que el comportamiento implementado en esta publicación es para comportamientos como la simulación de multitudes, en el que el objetivo principal de cada agente es evitar a otros agentes y lograr el objetivo. No determinan el camino más eficiente y más corto.
Requisitos técnicos
Requiere Unity 2017 instalado en un sistema con Windows 7 SP1 +, 8, 10 o Mac OS X 10.9+. El código de este artículo no funcionará en Windows XP y Vista, y las versiones de servidor de Windows y OS X no se han probado.
Los archivos de código para esta publicación se pueden encontrar en
GitHub .
Para aprender el código en acción, mira
este video .
Malla de navegación
Veamos cómo usar el generador de malla de navegación incorporado de Unity, que puede simplificar enormemente la búsqueda de rutas para agentes de IA. En las primeras etapas de Unity 5.x, la función NavMesh estuvo disponible para todos los usuarios, incluidos aquellos con licencias de edición personal, aunque anteriormente era una función solo para Unity Pro. Antes del lanzamiento de 2017.1, el sistema se actualizó para proporcionar un flujo de trabajo basado en componentes, pero dado que requiere un paquete descargable adicional, que en el momento de la escritura solo está disponible en la versión de vista previa, nos adheriremos al flujo de trabajo estándar basado en escenas. No se preocupe, los conceptos de ambos enfoques son similares, y cuando la implementación final finalmente llegue a 2017.x, no debería haber cambios significativos.
Obtenga más información sobre el sistema de componentes NavMesh en Unity en
GitHub .
Ahora exploraremos todas las posibilidades que este sistema puede ofrecernos. Para buscar rutas de IA, la escena debe presentarse en un formato específico; en un mapa 2D, se usa una cuadrícula (matriz) bidimensional para buscar rutas utilizando el algoritmo A *. Los agentes de IA necesitan saber dónde están los obstáculos, especialmente los estáticos. Lidiar con las colisiones entre objetos dinámicamente en movimiento es otro problema comúnmente conocido como comportamiento de dirección. Unity tiene una herramienta incorporada para generar NavMesh, que representa la escena en un contexto conveniente para que los agentes de IA encuentren la ruta óptima hacia el objetivo. Para comenzar, abra un proyecto de demostración y vaya a la escena NavMesh.
Tarjeta de estudio
Después de abrir la escena de demostración NavMesh, debería verse como en la captura de pantalla:
Obstáculo y escena de pendienteEsta será nuestra caja de arena para explicar y probar la funcionalidad del sistema NavMesh. El esquema general es similar a un juego en el género de RTS (estrategia en tiempo real). Conducimos un tanque azul. Haga clic en diferentes puntos para que el tanque se mueva hacia ellos. El indicador amarillo es el objetivo actual del tanque.
Navegación estática
Primero, debe decir que debe marcar toda la geometría de la escena, horneada en NavMesh, como
estática de navegación . Es posible que haya visto esto antes, por ejemplo, en el sistema de mapa de iluminación Unity. Hacer que los objetos del juego estén estáticos es muy simple, simplemente marque la casilla
Estático para ver todas sus propiedades (navegación, iluminación, selección, procesamiento por lotes, etc.), o use la lista desplegable para especificar propiedades. La casilla de verificación se encuentra en la esquina superior derecha del inspector de los objetos seleccionados.
Navegación de propiedad estáticaEsto se puede hacer individualmente para diferentes objetos o, si tiene una jerarquía incorporada de objetos de juego, aplique el parámetro al objeto principal, después de lo cual Unity ofrecerá aplicarlo a todos los objetos secundarios.
Asar una malla de navegación
Para toda la escena, las opciones de navegación navmesh se aplican mediante la ventana de
navegación . Esta ventana se puede abrir yendo a
Ventana |
La navegación Al igual que cualquier otra ventana, puede desconectarse para moverse libremente o repararse. En nuestras capturas de pantalla, se muestra como una pestaña acoplada junto a la jerarquía, pero puede colocar esta ventana en cualquier lugar conveniente.
Al abrir la ventana, verá pestañas individuales. Se verá más o menos así:
Ventana de navegaciónEn nuestro caso, la captura de pantalla anterior muestra la pestaña
Hornear , pero en su editor, cualquier pestaña se puede seleccionar de forma predeterminada.
Veamos cada una de las pestañas, comenzando desde la izquierda y moviéndose hacia la derecha. Comencemos con la pestaña
Agentes , que parece que la captura de pantalla muestra:
Pestaña AgentesSi está trabajando en otro proyecto, es posible que algunas de las configuraciones sean diferentes de las que configuramos para el proyecto de ejemplo que se muestra en la captura de pantalla. En la parte superior de la pestaña hay una lista donde puede agregar nuevos tipos de agentes haciendo clic en el botón
+ . Puede eliminar agentes adicionales seleccionándolos y haciendo clic en el botón
- . La ventana muestra claramente lo que hacen varias configuraciones al cambiarlas. Veamos qué hace cada una de las configuraciones:
- Nombre: nombre del tipo de agente que se muestra en la lista desplegable Tipos de agente.
- Radio: Puedes considerarlo como el "espacio personal" de un agente. Los agentes tratarán de evitar el contacto demasiado cercano con otros agentes en función de este valor, ya que se usa para evitarlo.
- Altura: como puede suponer, esta configuración establece la altura del agente que utiliza para evitar verticalmente (por ejemplo, al pasar por debajo de los objetos).
- Altura del paso: este valor determina qué altura puede subir el agente.
- Pendiente máxima: como veremos en la siguiente sección, este valor determina el ángulo máximo en el que el agente puede subir. Con este parámetro, puede hacer que las pendientes pronunciadas del mapa sean inaccesibles para el agente.
A continuación, tenemos la pestaña
Áreas , que se parece a la que se muestra en esta captura de pantalla:
Como puede ver en la captura de pantalla, Unity proporciona varios tipos de áreas que no se pueden cambiar:
Caminable ,
No caminable y
Saltar . Además de nombrar y crear nuevas áreas, puede asignar a estas áreas el costo de moverse por ellas.
Las áreas tienen dos propósitos: hacer que las áreas sean accesibles o inaccesibles para el agente, y marcar las áreas como menos deseables en términos de costos de viaje. Por ejemplo, puedes desarrollar un juego de rol en el que los demonios enemigos no puedan entrar en áreas marcadas como "terreno consagrado". También puede marcar algunas áreas del mapa como un "atolladero" o "pantano", que el agente evitará debido al alto costo de la mudanza.
La tercera pestaña
Bake es probablemente la más importante. Le permite crear NavMesh para la escena. Ya debería estar familiarizado con algunas de las opciones. La pestaña
Hornear se ve así:
Pestaña hornearLas opciones de tamaño de agente en esta pestaña determinan cómo los agentes interactuarán con el entorno, mientras que las opciones en la pestaña
Agentes controlan las interacciones con otros agentes y objetos en movimiento. Pero controlan los mismos parámetros, por lo que los omitiremos.
La altura de caída y la
distancia de salto controlan qué tan lejos puede "saltar" el agente para llegar a la parte de NavMesh que no está directamente relacionada con aquella en la que el agente se encuentra actualmente. Consideraremos esto con más detalle a continuación, por lo que si no está seguro, aún no puede estudiar estos parámetros.
Además, hay opciones avanzadas que generalmente están ocultas de forma predeterminada. Para expandir estas opciones, simplemente haga clic en el triángulo desplegable junto al encabezado
Avanzado .
El tamaño manual de Voxel puede considerarse como una configuración de "calidad". Cuanto más pequeño sea el tamaño, más detalles se almacenarán en la malla.
El área de región mínima se usa para omitir plataformas o superficies para hornear por debajo del umbral seleccionado.
La malla de altura nos proporciona datos verticales más detallados al hornear una malla. Por ejemplo, esta opción le permite mantener la ubicación correcta del agente al subir escaleras.
El botón
Borrar elimina todos los datos NavMesh de la escena, y el botón
Hornear crea una malla para la escena. El proceso de horneado es bastante rápido. Mientras tenga una ventana seleccionada, puede observar la generación de NavMesh con el botón
Bake en la ventana de escena. Hagamos clic en el botón
Hornear para ver los resultados. En nuestra escena de ejemplo, terminamos con algo similar a esta captura de pantalla:
Las áreas azules muestran NavMesh. A continuación volveremos a esto. Mientras tanto, pasemos a la última pestaña:
Objeto , que se ve así:
Los tres botones que se muestran en la captura de pantalla anterior (
Todos ,
Renderizadores de malla y
Terrenos ) se utilizan como filtros de escena. Son útiles cuando se trabaja en escenas complejas con muchos objetos en la jerarquía. Al seleccionar una opción, se filtra el tipo correspondiente de la jerarquía, lo que facilita su selección. Puede usar los botones para explorar su escena en busca de objetos que desee marcar como navegación estática.
Usando Nav Mesh Agent
Ahora que hemos configurado la escena con NavMesh, necesitamos una forma para que el agente use esta información. Afortunadamente para nosotros, Unity tiene un componente
Nav Mesh Agent que puedes arrastrar a un personaje. En nuestra escena de ejemplo, hay un objeto de juego llamado
Tank , al que ya se ha adjuntado un componente. Mire la jerarquía y verá algo como esto:
Hay bastantes parámetros aquí, y no consideraremos todo, porque son bastante claros, y la descripción se puede encontrar en la documentación oficial de Unity. Pero mencionaremos las cosas principales:
- Tipo de agente : ¿Recuerda la pestaña Agentes en la ventana de navegación ? Los tipos de agentes asignables se pueden seleccionar aquí.
- Auto Traverse Off Mesh Link : esta opción permite a los agentes usar automáticamente la función Off Mesh Links , que discutiremos a continuación.
- Máscara de área : aquí puede seleccionar las áreas configuradas en la pestaña Áreas de la ventana de navegación .
Eso es todo Este componente hace el 90% del trabajo duro para nosotros: allanando el camino, evitando obstáculos, etc. Lo único que necesita es transferir el punto objetivo al agente. Miremos este problema.
Ajuste del punto objetivo
Después de configurar el agente de IA, necesitamos una forma de decirle a dónde ir. En nuestro proyecto de ejemplo, hay un script llamado
Target.cs que realiza exactamente esta tarea.
Esta es una clase simple que hace tres cosas:
- "Dispara" el haz de la cámara a la posición del mouse en el mundo
- Actualiza la posición del marcador
- Actualiza la propiedad de destino para todos los agentes NavMesh.
El código es bastante simple. Toda la clase es la siguiente:
using UnityEngine; using UnityEngine.AI; public class Target : MonoBehaviour { private NavMeshAgent[] navAgents; public Transform targetMarker; private void Start () { navAgents = FindObjectsOfType(typeof(NavMeshAgent)) as NavMeshAgent[]; } private void UpdateTargets ( Vector3 targetPosition ) { foreach(NavMeshAgent agent in navAgents) { agent.destination = targetPosition; } } private void Update () { if(GetInput()) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; if (Physics.Raycast(ray.origin, ray.direction, out hitInfo)) { Vector3 targetPosition = hitInfo.point; UpdateTargets(targetPosition); targetMarker.position = targetPosition; } } } private bool GetInput() { if (Input.GetMouseButtonDown(0)) { return true; } return false; } private void OnDrawGizmos() { Debug.DrawLine(targetMarker.position, targetMarker.position + Vector3.up * 5, Color.red); } }
Aquí se producen las siguientes acciones: en el método
Start , inicializamos la matriz
navAgents utilizando el método
FindObjectsOfType () .
El método
UpdateTargets () pasa a través de nuestra matriz
navAgents y establece el punto de destino para ellos en el
Vector3 dado. Esta es la clave del código. Puede usar cualquier mecanismo para obtener el punto objetivo y para que el agente se mueva allí, simplemente configure el campo
NavMeshAgent.destination ; El agente hará el resto.
En nuestro ejemplo, los clics se usan para moverse, por lo que cuando un jugador hace clic en el mouse, liberamos el rayo de la cámara al mundo en la dirección del cursor del mouse, y si se cruza con algo, le asignamos un punto de colisión al nuevo agente
targetPosition . También ajustamos el marcador de objetivo en consecuencia para visualizar fácilmente el destino en el juego.
Para probar la operación, debe hornear NavMesh de acuerdo con la descripción de la sección anterior, luego iniciar el modo Reproducir y seleccionar cualquier área en el mapa. Si hace clic muchas veces, puede ver que el agente no puede alcanzar algunas áreas: la parte superior de los cubos rojos, la plataforma superior y la plataforma en la parte inferior de la pantalla.
Los cubos rojos son demasiado altos. La pendiente que conduce a la plataforma más alta es demasiado pronunciada para nuestra configuración de
Max Slope , y el agente no puede subirla. Las siguientes capturas de pantalla muestran cómo la configuración de
Pendiente máxima afecta a NavMesh:
NavMesh con pendiente máxima = 45Si cambia el valor de
Pendiente máxima a algo así como
51 , y luego vuelve a hacer clic en el botón
Hornear para hornear NavMesh, los resultados serán los siguientes:
NavMesh con pendiente máxima = 51Como puede ver, podemos personalizar el diseño del nivel, haciendo que áreas enteras sean inaccesibles al cambiar un solo parámetro. Esto puede ser útil, por ejemplo, cuando tiene una plataforma o repisa que requiere una cuerda, escalera o elevador para subir. ¿O tal vez una habilidad especial, por ejemplo, la capacidad de escalar?
Aplicación fuera de enlaces de malla
Puede notar que hay dos pausas en nuestra escena. Nuestro agente puede entrar en el primero, pero el que está en la parte inferior de la pantalla está demasiado lejos. Estos cálculos no son completamente arbitrarios.
Los enlaces Off Mesh esencialmente crean un puente a través de los espacios entre segmentos NavMesh no relacionados. Estos enlaces se pueden ver en el editor:
Los círculos azules con líneas de conexión son conexiones.Unity puede generar estos enlaces de dos maneras. El primero ya lo hemos considerado. ¿Recuerdas el valor de
Distancia de salto en la pestaña
Hornear de la ventana de
navegación ? Unity usa automáticamente ese valor para generar estos enlaces cuando hornea NavMesh. Intente cambiar el valor en nuestra escena de prueba a 5 y hornee nuevamente. ¿Ves - las plataformas están ahora conectadas? Esto se debe a que las mallas ahora están dentro del nuevo umbral especificado.
Cambie el valor a 2 nuevamente y hornee. Ahora veamos la segunda forma. Crea las esferas que se usarán para conectar las dos plataformas. Colóquelos aproximadamente como se muestra en la captura de pantalla:
Ya puede ver lo que está sucediendo, pero analicemos el proceso que les permite conectarse. En nuestro caso, llamé a la esfera en el
comienzo derecho y a la esfera en el
extremo izquierdo. Pronto entenderás por qué. A continuación, agregué el componente
Off Mesh Link a la plataforma a la derecha (en relación con la captura de pantalla anterior). Notará que el componente tiene campos de
inicio y
fin . Como puede suponer, arrastraremos las esferas creadas previamente a las ranuras correspondientes: la esfera de inicio en el campo de
inicio y la esfera de fin en el campo de
fin . El inspector se verá así:
El valor de la
anulación de
costos se tiene en cuenta cuando se le da un valor positivo. Aplica un factor de costo cuando se usa esta relación en lugar de una ruta más rentable hacia el objetivo.
Bi direccional si es verdadero permite que el agente se mueva en ambas direcciones. Para crear enlaces con tráfico unidireccional, puede deshabilitar este valor. El valor
Activado se usa según su nombre. Si es falso, el agente ignora esta asociación. Puedes activarlo y desactivarlo para crear escenarios de juego en los que, por ejemplo, un jugador debe presionar un interruptor para activar una conexión.
Para habilitar esta relación, no es necesario volver a hornear. Mire su NavMesh y verá que se ve exactamente como en la captura de pantalla:
Como puede ver, la brecha más pequeña todavía se conecta automáticamente, y ahora tenemos una nueva conexión generada por el componente
Off Mesh Link entre las dos esferas. Inicie el modo Play y haga clic en la plataforma lejana. Como se esperaba, el agente ahora puede navegar a la plataforma desconectada:
En los niveles de su juego, es posible que necesite cambiar estos parámetros para lograr los resultados deseados, pero una combinación de estas características le proporciona una herramienta conveniente y lista para usar. Puedes crear rápidamente un juego simple usando la funcionalidad NavMesh.
Este tutorial es parte de la Programación de IA de juegos de Unity 2017 - Tercera edición de Ray Barrera, Aung Sithu Kyaw y Thet Naing Swe.