¿Cómo acceden los agentes de IA a la información sobre su entorno?

9

Esta podría ser una pregunta trivial, pero tengo problemas para entender esto. Agradecería mucho tu ayuda.

En el desarrollo de juegos con diseño orientado a objetos, quiero entender cómo los agentes de IA acceden a la información que necesitan del mundo del juego para realizar sus acciones.

Como todos sabemos, en los juegos muy a menudo los agentes de IA necesitan 'percibir su entorno' y actuar de acuerdo con lo que sucede a su alrededor. Por ejemplo, un agente podría estar programado para perseguir al jugador si él / ella se acerca lo suficiente, evitar obstáculos mientras se mueve (utilizando el comportamiento de dirección para evitar obstáculos), etc.

Mi problema es que no estoy seguro de cómo hacerlo. ¿Cómo puede un agente de IA acceder a la información que necesita sobre el mundo del juego?

Un posible enfoque es que los agentes simplemente solicitan la información que necesitan directamente del mundo del juego.

Hay una clase llamada GameWorld. Maneja lógica de juego importante (bucle de juego, detección de colisión, etc.), y también contiene referencias a todas las entidades en el juego.

Podría hacer de esta clase un Singleton. Cuando un agente necesita información del mundo del juego, simplemente la obtiene directamente de la instancia de GameWorld.

Por ejemplo, un agente puede ser programado para Seekel jugador cuando él / ella está cerca. Para hacer esto, el agente debe obtener la posición del jugador. Por lo que simplemente puede solicitar directamente: GameWorld.instance().getPlayerPosition().

Un agente también podría obtener la lista de todas las entidades en el juego y analizarla según sus necesidades (para descubrir qué entidades están cerca o cualquier otra cosa): GameWorld.instance().getEntityList()

Este es el enfoque más simple: los agentes se comunican directamente con la clase GameWorld y obtienen la información que necesitan. Sin embargo, este es el único enfoque que conozco. ¿Hay alguno mejor?

¿Cómo diseñaría esto un desarrollador de juegos experimentado? ¿Es ingenuo el enfoque de "obtener una lista de todas las entidades y buscar lo que necesite"? ¿Qué enfoques y mecanismos existen para permitir que los agentes de IA accedan a la información que necesitan para realizar sus acciones?

Aviv Cohn
fuente
Si tiene acceso a una suscripción a GDCVault, hubo una excelente charla en 2013 llamada "Creación de la IA para el mundo vivo, la respiración de la absolución de Hitman", que entra en su modelo de conocimiento de IA en detalle.
DMGregory

Respuestas:

5

Lo que usted describe es un modelo clásico "pull" para consultar el mundo. La mayoría de las veces, esto funciona bastante bien, especialmente para juegos con IA básica (que es la mayoría). Sin embargo, hay un par de puntos que debes considerar que podrían ser desventajas:

  • Probablemente quieras duplicar el búfer. Ver patrones de programación de juegos sobre el tema . Al solicitar siempre los datos directamente del mundo, puede obtener condiciones raras de carrera en las que el resultado depende del orden en que se llame la IA. Si esto es importante para tu juego, debes determinarlo. Un posible resultado es que sesga el juego a quien va "primero" o "último", haciendo que el modo multijugador sea injusto.

  • A menudo puede ser mucho más eficiente para las solicitudes por lotes, especialmente para ciertas estructuras de datos. Aquí puede hacer que cada agente de IA que quiera buscar obstáculos cree un "objeto de consulta" y lo registre con un obstáculo central único. Luego, antes del ciclo principal de AI, todas las consultas se ejecutan contra la estructura de datos, lo que mantiene la estructura de datos de obstáculos más en caché. Luego, durante la parte de IA, cada agente procesa los resultados de su consulta, pero no se le permite hacer más directamente. Al final del marco, los objetos AI actualizan las consultas con su nueva ubicación, o las agregan o eliminan. Esto es similar al diseño orientado a datos .

    Tenga en cuenta que esto básicamente hace doble búfer almacenando el resultado de las consultas en un búfer. También requiere que anticipe si necesita hacer una consulta antes del marco. Este es un modelo "push", porque los agentes declaran en qué tipo de actualizaciones están interesados ​​(al hacer un objeto de consulta correspondiente), y estas actualizaciones se envían a ellos. Tenga en cuenta que también puede hacer que el objeto de consulta contenga una devolución de llamada, en lugar de almacenar todos los resultados para un marco.

  • Finalmente, es probable que desee utilizar interfaces o componentes para sus objetos de búsqueda en lugar de herencia, lo cual está bien documentado en otros lugares. Iterar sobre una lista de Entitiescomprobación instanceOfes, probablemente, una receta para el código bastante frágil, el momento en que quiere tanto StaticObjecty MovingObjectser Healable. (a menos que instanceOffuncione para interfaces en el idioma que elija).


fuente
5

Si la IA es costosa, el rendimiento suele ser el factor determinante en la arquitectura.

Para aliviar sus preocupaciones sobre los modelos de acceso a datos, consideremos algunos ejemplos diferentes de IA, tanto dentro como fuera de la industria de los juegos, trabajando desde lo que está más alejado de la navegación humana hasta lo que nos es más familiar.

(Cada ejemplo supone una única actualización lógica global).

  • A * camino más cortoCada IA ​​calcula el estado del mapa con fines de búsqueda de ruta. A * requiere que cada IA ​​ya conozca todo el entorno (local) en el que debe encontrar la ruta, por lo que debemos entregarle información sobre los obstáculos del mapa y el espacio (a menudo una matriz booleana 2D). A * es una forma especializada del algoritmo de Dijkstra, un algoritmo de búsqueda de gráfico abierto de ruta más corta; dichos enfoques devuelven listas que representan la ruta, y en cada paso, la IA simplemente selecciona el siguiente nodo en esta lista para avanzar, hasta que alcanza su objetivo o se requiere un nuevo cálculo (por ejemplo, debido a un cambio de obstáculo en el mapa). Sin este conocimiento, no se puede encontrar un camino más corto realista. A * es la razón por la cual las IA en los juegos de estrategia en tiempo real siempre saben cómo llegar del punto A al punto B, si existe una ruta. Volverá a calcular el camino más corto para cada IA ​​individual, porque la validez de la ruta se basa en la posición de aquellas IA que se han movido (y potencialmente bloquearon ciertas rutas) anteriormente. El proceso iterativo mediante el cual A * calcula los valores de las celdas durante la búsqueda de rutas es uno de convergencia matemática. Se podría decir que el resultado final se asemeja a un sentido del olfato mezclado con un sentido de la vista, pero en general es algo ajeno a nuestra mentalidad.

  • Difusión colaborativa También se encuentra en los juegos, esto se parece más al sentido del olfato, basado en la difusión de gases y partículas. El CD resuelve el problema del costoso reprocesamiento que se encuentra en A *: en cambio, se almacena un solo estado de mapa, procesado una vez por actualización para todas las IA, y cada IA ​​accede a los resultados por turno, para que realice su movimiento respectivo . Ya no hay una sola ruta (lista de celdas) devuelta por un algoritmo de búsqueda; más bien, cada IA ​​inspeccionará el mapa después de que se haya procesado, y se moverá a la celda adyacente que tenga el valor más alto. Esto se llama escalada . Sin embargo, la fase de procesamiento del mapa ya debetener acceso previo a la información del mapa, que aquí también contiene las ubicaciones de todos los cuerpos de IA. Por lo tanto, el mapa hace referencia a las IA, y luego las IA hacen referencia al mapa.

  • Visión por computadora y emisión de rayos + ruta más corta En la robótica de robots y drones, esto se está convirtiendo en la norma para determinar la extensión de los espacios por los que navega el robot. Esto permite a los robots construir un modelo volumétrico completo de su entorno, tal como lo haríamos a simple vista o incluso al sonido o al tacto (para ciegos o sordos), que el robot puede reducir a un gráfico topográfico mínimo (un poco como un gráfico de waypoint) utilizado en juegos con A *), sobre los cuales se pueden aplicar algoritmos de ruta más corta. En este caso, si bien la "visión" puede proporcionar una pista sobre el entorno inmediato, aún resulta enuna búsqueda gráfica que finalmente proporciona el camino hacia la meta. Esto está cerca del pensamiento humano: para llegar a la cocina desde el dormitorio, debo pasar por la sala de estar. El hecho de que ya los he visto y conozco sus espacios y sus portales, es lo que permite este movimiento calculado. Esta es una topología gráfica, a la que se aplica un algoritmo de ruta más corta, aunque incrustado en proteínas blandas en lugar de silicio duro.

Por lo tanto, puede ver que los dos primeros dependen de conocer el entorno en su totalidad. Esto, por el costo de evaluar un entorno desde cero, es común en los juegos. Claramente, el último es el más poderoso. Un robot equipado de esta manera (o, por ejemplo, una IA de juego que lee el búfer de profundidad de cada cuadro) podría navegar lo suficiente en cualquier entorno sin conocimiento previo de él. Como probablemente haya adivinado, también es, con mucho, el más costoso de los tres enfoques anteriores, y en los juegos generalmente no podemos permitirnos hacer esto por IA. Por supuesto, es mucho menos costoso en 2D que en 3D.

Puntos arquitectónicos

Queda claro anteriormente que no podemos suponer un solo patrón de acceso a datos correcto para AI; la elección depende de lo que intentes lograr. Acceder a la GameWorldclase directamente es absolutamente estándar: simplemente le proporciona información mundial. Esencialmente es su modelo de datos, y para eso están los modelos de datos. Singleton está bien para esto.

"obtén una lista de todas las entidades y busca lo que necesites"

Nada ingenuo sobre eso, en absoluto. Lo único que podría ser ingenuo es realizar más iteraciones de listas de las que necesita. En la detección de colisiones, evitamos esto usando, por ejemplo, cuadrúpedos para reducir el espacio de búsqueda. Mecanismos similares pueden aplicarse a la IA. Y si puede compartir el mismo ciclo para hacer varias cosas, hágalo, porque las ramas son costosas.

Ingeniero
fuente
Gracias por responder. Como soy un principiante en el desarrollo de juegos, creo que me atendré al simple enfoque de "obtener una lista del mundo del juego" por ahora :) Una pregunta: en mi pregunta describí la GameWorldclase como la clase que contiene referencias a todas las entidades del juego, y también contiene la mayor parte de la lógica importante del "motor": el bucle principal del juego, la detección de colisiones, etc. Es básicamente la "clase principal" del juego. Mi pregunta es: ¿Es este enfoque común en los juegos? ¿Tienes una 'clase principal'? ¿O debería separarlo en clases más pequeñas y tener una clase como una 'base de datos de entidades' que los objetos pueden sondear?
Aviv Cohn
@Prog De nada. Una vez más, no hay nada en los enfoques de IA anteriores (o cualquier otro, para el caso) que sugiera que su "obtener una lista del mundo del juego" sea de alguna manera, forma o forma arquitectónicamente incorrecta. La arquitectura de la IA debe ajustarse a las necesidades de la IA; pero esa lógica, como sugiere, debe modularizarse, encapsularse (en su propia clase) lejos de su arquitectura de aplicación más amplia . Sí, los subsistemas siempre deben factorizarse en módulos separados una vez que surjan tales preguntas. Su principio rector debe ser SRP .
Ingeniero
2

Básicamente tendría 2 formas de consultar información.

  1. cuando el AIState cambia porque detectó una colisión o cualquier caché, una referencia a cualquier objeto es importante. De esa manera, sabrá qué referencia necesita. Cuando haya otros sistemas que tengan que ejecutar búsquedas grandes en cada cuadro, recomendaría que se retroceda en ellos para que no tenga que realizar múltiples búsquedas. Entonces, la 'colisión' detectada con la zona que hace que una 'alerta' enemiga le envíe un mensaje / evento que lo registre con ese objeto si aún no lo está y cambie el estado del juego a uno que lo haga hacer su negocio basado en estar en ese estado de juego. Necesita un evento de algún tipo que le indique que haga cambios, simplemente pasaría una referencia a cualquier devolución de llamada que use para proporcionar esa información. Esto es más extensible que simplemente tener que lidiar con el jugador. Tal vez quieras que un enemigo persiga a otro enemigo u otro objeto. De esta manera, solo necesita cambiar la etiqueta con la que lo identifique.

  2. Con esa información, realizará una consulta a un sistema de búsqueda de ruta que usa A * o algún otro algoritmo para darle una ruta o puede usarlo con algún comportamiento de dirección. Tal vez una combinación de ambos o lo que sea. Básicamente, con la transformación de ambos, debería poder consultar su sistema de nodos o navmesh y hacer que le proporcione una ruta. Es probable que tu mundo de juego tenga muchas otras cosas además de encontrar caminos. Enviaría su consulta solo a pathfinding. También es probable que lo mejor sea agrupar estas cosas si tiene muchas consultas, ya que esto puede ser bastante intenso y el procesamiento por lotes ayudará al rendimiento.

    Transform* targetTransform = nullptr;
    EnemyAIState  AIState = EnemyAIState::Idle;
    void OnTriggerEnter(GameObject* go)
    {
       if(go->hasTag(TAG_PLAYER))
       {
       //Cache important information that will be needed during pursuit
       targetTransform = go->getComponent<Transform>();
       AIState = EnemyAIState::Pursue;
       }
    }
    
    void Update()
    {
       switch(AIState)
       {
          case EnemyAIState::Pursue:
           //Find position to move to
           Vector3 nextNode = PathSystem::Seek(
                              transform->position,targetTransform->position);
           /*Update the position towards the target by whatever speed the unit moves
             Depending on how robust your path system is you might want to raycast
             against obstacles it can't take into account or might clip the path.*/
            transform->Move((nextNode - transform->position).unitVector()*speed*Time::deltaTime());
            break;
        }
     }
usuario2927848
fuente