En esta conferencia, implementaremos un sistema GOAP en Unity.
Ahora puede descargar el archivo de inicio para seguirlo y
se llama GOAP. Código Postal.
Hay 2 archivos de inicio con esta conferencia ... ahí está el GOAP. zip y también hay
un maestro goap. Código Postal.
Goap-master es la versión completa de esta biblioteca GOAP que he tomado de la URL que puedes ver en la
pantalla.
Por lo tanto, tiene otros ejemplos y cosas que puede ver más
adelante y lo que hice con el GOAP es que lo eliminé todo lo que pude
y también lo modifiqué para permitir un Nav Mesh Agent porque usaremos un NavMesh para mostrar a nuestros agentes en la
pantalla entre sus diferentes destinos.
Ahora, si traes el GOAP. paquete zip en Unity, luego encontrarás ESTO cuando abras la escena
del pueblo.
Tengo un agente y él será un panadero.
Esta es su panadería ...
este es el molino de viento que suministra la harina ...
y luego por aquí, tenemos algunos mercados. Ahora, con cada uno de estos edificios he incluido
un cubo
(que he llamado 'Puerta') y puedes verlo en la Jerarquía. El agente necesita un lugar al que ir, para realizar la tarea y puede
hacer que vayan al edificio real para realizar la tarea, pero pensé que
sería bueno tener solo un pequeño marcador a dónde podrían ir, así
que es por eso que Los he agregado aquí y los verá en uso más adelante.
Ahora este agente está en un avión, que es un NavMesh.
Si abre su pestaña de Navegación, verá que NavMesh ya está aplicado.
El agente (que se llama Baker porque vamos a hacer que hornee pan) en la Jerarquía ya
tiene algunas cosas preparadas.
Entonces, antes que nada, tiene el Nav Mesh Agent allí, que es lo que todos usamos para navegar
por la escena.
También debe agregar a cualquier agente (que sea un agente GOAP) la secuencia de comandos 'Agente Goap' y esa secuencia de comandos del Agente Goap está en
los Activos en la carpeta GOAP.
Llegaremos a eso en un momento, eso ya está escrito para ti. Entonces, las cosas que tiene que escribir
que se encuentran en la parte superior del sistema GOAP son las secuencias de comandos que ejecutan el
agente en sí y lo llamamos Trabajador y el agente también tiene un Inventario y volveré y miraré dentro de aquellos en un segundo.
Vamos a los Activos y echemos un vistazo a la carpeta AI.
Encontrará que hay una carpeta de Máquina de estado finito (FSM).
Esta es la pequeña máquina de estado que solo conduce al agente de una ubicación a otra para
realizar las tareas.
No utiliza el sistema de máquina de estado integrado en Unity, todo se hace mediante código.
Es una cosa tan simple que realmente no necesita el Sistema de Animación para
configurar todos los diferentes estados y transiciones entre él porque esencialmente solo tiene que lidiar
con 2 estados.
Luego hay una carpeta GOAP. Vaya a la carpeta GOAP y aquí encontrará
todas las clases que cumplen con las diferentes partes de la arquitectura GOAP. Primero está el agente GOAP, que se encarga de las acciones básicas del
agente y se asegura de que va a ejecutar el Planificador, que está buscando Objetivos, que está
haciendo un seguimiento del Estado Mundial a medida que cambia.
Entonces tenemos una GoapAction.
Ahora, cada acción que agregue heredará de esta clase en particular y eso le permitirá crear un montón de
acciones, incluso si no se utilizan.
Así que recuerde: se puede agregar una acción en el sistema GOAP.
No está conectado a nada en absoluto, solo está sentado allí mismo y tiene condiciones previas y efectos, y lo que sucede
cuando hay un
objetivo es que el planificador reúne todas
estas acciones y trata de juntarlas. , para ver dónde se unen las Condiciones previas y los Efectos para
formar una cadena, que se convierte en su Plan. Por lo tanto, puede agregar tantas de estas
acciones a su agente como sea posible, pero si
no tienen condiciones previas y efectos que se unirán con otras acciones, nunca se
usarán.
Esa es una de las cosas que realmente debe tener en cuenta cuando diseña sus
acciones, pero lo solucionaremos cuando comencemos a hacer nuestras propias acciones y lo importante que es, para
que se encadenen.
El propio GoapPlanner reúne todas las acciones disponibles y las compara con las condiciones, el estado
del mundo y cualquier objetivo en particular y presenta su plan de acciones encadenado, luego lo informa
al agente que luego usa la máquina de estado finito para realizar las acciones a lo largo de la cadena.
Ahora el código que nos interesa y el código que escribimos para interactuar con ese sistema GOAP se encuentra actualmente en nuestro
Baker y ese es el script Worker y el script Inventory. El script de Inventario, si lo suelta
en el Inspector, verá que tiene niveles para Harina y Pan.
Estas son las cosas que tiene el agente, es como su 'mochila' o 'inventario'. Si le echas un vistazo a ese
código, es realmente bastante simple.
Todo lo que es, son algunos números enteros que contienen los
valores y estamos comenzando a que nuestro agente tenga 5 bolsas de harina.
Hay un OnGUI realmente simple allí, solo para imprimirlo en la pantalla, para que podamos ver estos valores
cambiar a medida que ejecutamos la simulación.
Ahora el código de trabajador que también está en nuestro agente heredará de nuestra interfaz GOAP aquí (IGoap), que es esencialmente
nuestro agente GOAP.
Está en ello, obviamente, nuestro NavMeshAgent que necesitamos para impulsarlo.
También hay un Vector de Destino3 anterior que se almacena por motivos de navegación, que verá en un momento y
luego también tenemos
un enlace a nuestro sistema de inventario que se encuentra en este agente.
Esas 2 cosas (el NavMeshAgent y el Inventario) se configuran dentro del Inicio.
Así que nos agarramos a esos 2 componentes, para
que no tengamos que seguir haciendo esto en todo el código.
Ahora, avanzando a través de este código, llegamos a algunas de las partes más importantes de este agente
y estos son los 2 métodos siguientes: GetWorldState y CreateGoalState. GetWorldState inyectará en el sistema GOAP,
el estado del mundo.
Ahora, para esto, vamos a utilizar estados 'verdadero'
y 'falso' como vimos originalmente en la conferencia anterior y los vamos a establecer aquí.
Por lo tanto, esto se seguirá ejecutando y actualizando para su agente para que el sistema de planificación pueda verificar si se
han alcanzado o no ciertos estados.
Ahora, en este caso, inicialmente, estamos configurando nuestro estado mundial 'hasFlour' en Verdadero o Falso
según la invitación. harinaLevel. Antes de continuar, me gustaría explicar la estructura
de las Condiciones previas y los Efectos, ya que son esencialmente el pegamento que mantiene todas las Acciones juntas en la cadena.
Todos se almacenan en un HashSet en el formato 'cadena, objeto'.
En este caso, los estamos representando con una cadena que explica el Estado y luego un Verdadero o Falso.
Por ejemplo, ("hasFood", verdadero) ("hasBread", falso). Verá estos conjuntos de acciones más adelante y son
realmente las condiciones que unirán cada acción, por lo que para cada condición previa de las
acciones, debe asegurarse de tener otra acción que tenga un efecto que esencialmente las magnetice y emparejarlos juntos,
para que puedan formar una cadena.
Si no los tiene en ninguna parte de su conjunto de acciones, esa acción no se utilizará y es posible que
el Planificador no pueda encontrar un plan.
Entonces, volvamos a nuestro código. Donde colocamos estas Precondiciones y Efectos es dentro de
estos 2 métodos de los que acabamos de hablar, entre la declaración inicial para el conjunto 'worldData' y también para el conjunto 'objetivo'.
Así que esto es todos los estados del mundo, como dije. Se actualiza todo el tiempo, por lo que
esta función se llama todo el tiempo para seguir verificando que este 'hasFlour', por ejemplo, sea Verdadero o
Falso y es por eso que podemos poner estas variables aquí, que podemos cambiar.
Este es el nivel de harina en el inventario y no va a decir que tienes harina a menos que
el nivel de harina sea mayor que 1.
Entonces, en este caso, no considera que tenga suficiente harina para hacer nada hasta que tenga 2 bolsas
de harina.
Eso establece el límite de cuántas bolsas de harina se consideran "tener suficiente harina para
hornear".
Ahora, si tiene otras condiciones en los Estados Mundiales que desea hacer, que
tienen un valor variable, puede agregarlas dentro de aquí.
No puede poner los valores de tipo variable como este dentro de sus acciones porque no funcionará.
Las acciones tienen que coincidir exactamente cuando se
planifica y, si no lo hacen, se bloqueará todo el sistema.
Entonces, lo veremos en un momento de todos modos.
Así que ese es tu mundoData.
Ahora los objetivos en sí van en
el medio donde declaras el conjunto de objetivos y donde los devuelves.
Así que dentro de aquí, quiero que agreguen un nuevo objetivo para nuestro Baker y será así.
Así que aquí está nuestra pequeña cadena aquí y aquí está nuestro valor Verdadero o Falso.
Ahora, este es un objetivo global para nuestro agente y simplemente
se llamará "doJob", por
lo que "doJob" es Verdadero y seguirá forzándolo en el agente y querrá seguir asegurándose
de que aborde esto Objetivo particular.
Así que solo tendremos el 1 gol aquí.
Entonces agregue eso y luego más abajo echemos un vistazo al resto del código.
Tenemos el código para mover el agente.
Este es el código de tipo típico de NavMeshAgent y lo que
hace es un par de cosas.
En primer lugar, verifica nuestro Destino anterior (recuerde que Vector3 en la parte superior de este código), verifica
si eso no es lo mismo a dónde debemos ir a continuación porque si un NavMeshAgent está en su destino cuando se
le da un nuevo objetivo, entonces no planificará una ruta y eso se convierte en un problema para determinar
si hemos llegado a nuestro próximo destino para realizar nuestra próxima tarea.
Entonces, si la tarea está en la misma posición que la última tarea, esto se encargará de eso
porque el agente no necesita moverse físicamente por el entorno, momento en el que
establecemos nextAction. setInRange a verdadero.
Ahora, cada acción no se ejecutará A MENOS QUE su agente esté dentro del rango de donde eso debe suceder.
Entonces eso es lo que está haciendo esta línea aquí.
Entonces, si no estamos obligados a permanecer en el mismo destino (tenemos que ir a otro lugar), usaremos nuestra NavMesh
con SetDestination usando la siguiente Acción. objetivo. transformar. posición para empujarnos a
la siguiente ubicación y luego ejecutaremos controles como lo hicimos en el pasado con NavMeshes para verificar si
hemos llegado a nuestro destino.
Entonces, si tenemos un camino y estamos a una distancia de 2, entonces hemos llegado a nuestro destino.
Esta es básicamente su precisión de lo cerca que desea llegar y luego
hacemos lo mismo.
Así que estamos dentro
del rango y luego actualizamos nuestro Destino anterior, de modo que esto solo registre dónde estamos y regresemos
Verdadero para que el sistema sepa que estamos en nuestro destino y que estamos listos para realizar la próxima acción.
Todo bien.
Entonces, al pasar a la Actualización, no necesita nada dentro de la Actualización, pero lo
que he hecho aquí es solo agregar un poco de código que ayudará a su agente a reducir la
velocidad y acelerar mejor en las esquinas y realmente puede aplicar esto Bloque if completo a cualquier código de Agente de
Nav Mesh
que tenga, si desea que sus agentes tomen esquinas cerradas.
Así que desarrollé esto para otra cosa en la que estaba trabajando y pensé en tirarlo aquí.
Esto funciona muy bien si estás tratando de hacer que tus agentes se muevan por las calles y carreteras
y quieres que permanezcan allí y no se desvíen hacia la acera, que es lo que harán
si van bastante rápido .
Esto los ralentizará en las esquinas para ti, así
que toma nota de eso y luego
más abajo tenemos algunos métodos que en realidad no estamos implementando en absoluto, pero solo los necesitamos allí, de lo
contrario, se quejarán
y más adelante cuando Si se siente más cómodo con este sistema, puede
regresar y poner su propio código aquí.
Entonces, antes que nada, hay uno que se llama cuando falla el plan ... uno cuando se encuentra el plan ... cuando tu acción haya
terminado ...
y cuando su plan ha abortado. Entonces, si desea explorar estos, puede poner las declaraciones
de depuración aquí para ver cuándo
se ejecutan realmente, pero no las vamos a usar.
Okay.
En este punto, si presiona Reproducir, verá en la consola (si va allí) los mensajes que salen del
sistema.
Entonces, estos mensajes están en declaraciones de depuración en toda la biblioteca GOAP que está utilizando y puede encontrarlos si
echa un vistazo, pero le darán una idea de lo que está sucediendo.
Entonces, lo que estamos obteniendo en este momento es que ha encontrado acciones.
Ahora no ha encontrado ninguna acción porque no hemos puesto ninguna acción
allí, pero todavía ha encontrado un conjunto de acciones vacío y luego está tratando de hacer un plan que satisfaga ese
objetivo "doJob" y no puede encontrar uno , obviamente, así que solo hace eso (SIN PLAN) y luego
le da este mensaje de 'Plan fallido'.
Así sabrá a medida que avance y programe este sistema, cuando las cosas no funcionen. Ok, dejaremos esta conferencia aquí y en
la próxima conferencia comenzaremos a crear acciones para nuestro agente.