In che modo gli agenti di intelligenza artificiale accedono alle informazioni sul loro ambiente?


9

Potrebbe essere una domanda banale, ma ho difficoltà a capirlo. Apprezzerei molto il tuo aiuto.

Nello sviluppo del gioco usando la progettazione orientata agli oggetti, voglio capire come gli agenti di intelligenza artificiale accedono alle informazioni di cui hanno bisogno dal mondo di gioco per eseguire le loro azioni.

Come tutti sappiamo, nei giochi molto spesso gli agenti di intelligenza artificiale devono "percepire il loro ambiente" e agire in base a ciò che accade intorno a loro. Ad esempio, un agente potrebbe essere programmato per inseguire il giocatore se si avvicina abbastanza, evitare ostacoli durante lo spostamento (usando il comportamento di guida Evitamento ostacoli), ecc.

Il mio problema è che non sono sicuro di come farlo. Come può un agente AI accedere alle informazioni di cui ha bisogno sul mondo di gioco?

Un possibile approccio è che gli agenti richiedano semplicemente le informazioni di cui hanno bisogno direttamente dal mondo di gioco.

C'è una classe chiamata GameWorld. Gestisce importanti logiche di gioco (loop di gioco, rilevamento delle collisioni, ecc.) E contiene anche riferimenti a tutte le entità del gioco.

Potrei rendere questa classe un Singleton. Quando un agente ha bisogno di informazioni dal mondo di gioco, le ottiene semplicemente direttamente dall'istanza di GameWorld.

Ad esempio, un agente potrebbe essere programmato per Seekil giocatore quando è vicino. Per fare ciò, l'agente deve ottenere la posizione del giocatore. Così può semplicemente chiedere direttamente: GameWorld.instance().getPlayerPosition().

Un agente potrebbe anche semplicemente ottenere l'elenco di tutte le entità nel gioco e analizzarlo per le sue esigenze (per capire quali entità sono vicine o altro): GameWorld.instance().getEntityList()

Questo è l'approccio più semplice: gli agenti contattano direttamente la classe GameWorld e ottengono le informazioni di cui hanno bisogno. Tuttavia, questo è l'unico approccio che conosco. ce n'è uno migliore?

In che modo uno sviluppatore di giochi con esperienza progetterebbe questo? L 'approccio "ottenere un elenco di tutte le entità e cercare ciò di cui hai bisogno" è ingenuo? Quali approcci e meccanismi ci sono per consentire agli agenti di intelligenza artificiale di accedere alle informazioni di cui hanno bisogno per eseguire le loro azioni?


Se hai accesso a un abbonamento GDCVault, nel 2013 si è tenuto un eccellente discorso dal titolo "Creare l'IA per il mondo vivente e respiratorio di Hitman Absolution" che approfondisce il loro modello di conoscenza dell'IA.
DMGregory

Risposte:


5

Quello che descrivi è un classico modello "pull" per interrogare il mondo. Il più delle volte, funziona abbastanza bene, specialmente per i giochi con AI base (che è la maggior parte). Tuttavia, ci sono un paio di punti che dovresti considerare che potrebbero essere aspetti negativi:

  • Probabilmente vuoi raddoppiare il buffer. Vedi gli schemi di programmazione del gioco sull'argomento . Richiedendo sempre i dati direttamente dal mondo, è possibile ottenere condizioni di gara strane in cui il risultato dipende dall'ordine in cui viene chiamato l'IA. È importante determinare se questo è importante per il tuo gioco. Un possibile risultato è che distorce il gioco a chiunque vada "primo" o "ultimo", rendendo ingiusto il multiplayer.

  • Spesso può essere molto più efficiente per le richieste batch, in particolare per determinate strutture di dati. Qui puoi fare in modo che ogni agente AI che desideri cercare ostacoli crei un "oggetto query" e lo registri con un ostacolo centrale singleton. Quindi, prima del ciclo AI principale, tutte le query vengono eseguite sulla struttura dei dati, il che mantiene la struttura dei dati degli ostacoli più nella cache. Quindi, durante la parte AI, ogni agente elabora i risultati delle query, ma non è possibile renderli più direttamente. Alla fine del frame, gli oggetti AI aggiornano le query con la loro nuova posizione oppure le aggiungono o rimuovono. Questo è simile al design orientato ai dati .

    Si noti che in pratica questo fa doppio buffer memorizzando il risultato delle query in un buffer. Richiede inoltre di prevedere se è necessario eseguire una query nel frame prima. Questo è un modello "push", perché gli agenti dichiarano a quale tipo di aggiornamenti sono interessati (creando un oggetto query corrispondente) e questi aggiornamenti vengono inviati a loro. Nota che puoi anche avere l'oggetto query contenente un callback, piuttosto che archiviare tutti i risultati per un frame.

  • Infine, probabilmente vorrai usare interfacce o componenti per i tuoi oggetti ricercabili piuttosto che l'ereditarietà, che è ben documentata altrove. Iterare su un elenco di Entitiescontrolli instanceOfè probabilmente una ricetta per un codice piuttosto fragile, nel momento in cui vuoi sia StaticObjecte MovingObjectsia Healable. (a meno che non instanceOffunzioni per le interfacce nella tua lingua preferita).


5

L'intelligenza artificiale è costosa, le prestazioni sono spesso il fattore trainante dell'architettura.

Per alleviare le tue preoccupazioni riguardo ai modelli di accesso ai dati, consideriamo alcuni diversi esempi di intelligenza artificiale sia all'interno che all'esterno del settore dei giochi, lavorando da quello più lontano dalla navigazione umana a ciò che ci è più familiare.

(Ogni esempio presuppone un singolo aggiornamento logico globale.)

  • Un * percorso più breveOgni AI calcola lo stato della mappa per scopi di tracciamento dei percorsi. A * richiede che ogni AI conosca già l'intero ambiente (locale) in cui deve trovare il percorso, quindi dobbiamo consegnare le informazioni sugli ostacoli e lo spazio della mappa (spesso un array booleano 2D). A * è una forma specializzata dell'algoritmo di Dijkstra, un algoritmo di ricerca a grafico aperto di percorso più breve; tali approcci restituiscono elenchi che rappresentano il percorso e, ad ogni passo, l'IA seleziona semplicemente il nodo successivo in questo elenco a cui avanzare, fino a quando non raggiunge il suo obiettivo o è necessario il ricalcolo (ad esempio a causa di una modifica dell'ostacolo della mappa). Senza questa conoscenza, non è possibile trovare alcun percorso più breve realistico. A * è il motivo per cui gli IA nei giochi RTS sanno sempre come andare dal punto A al punto B - se esiste un percorso. Ricalcola il percorso più breve per ogni singola IA, perché la validità del percorso si basa sulla posizione di quegli IA che hanno spostato (e potenzialmente bloccato determinati percorsi) in precedenza. Il processo iterativo mediante il quale A * calcola i valori delle celle durante l'individuazione del percorso è uno di convergenza matematica. Si potrebbe dire che il risultato finale assomiglia a un senso dell'olfatto mescolato con un senso della vista, ma in tutto è in qualche modo estraneo alla nostra mentalità.

  • Diffusione collaborativa Anche nei giochi, questo è molto simile a un olfatto, basato sulla diffusione di gas e particolati. Il CD risolve il problema della costosa rielaborazione trovata in A *: invece, viene memorizzato un singolo stato della mappa, elaborato una volta per aggiornamento per tutti gli IA, e i risultati sono a loro volta accessibili da ogni IA a sua volta, affinché esegua la rispettiva mossa . Non è più un singolo percorso (elenco di celle) restituito da un algoritmo di ricerca; piuttosto, ogni AI ispezionerà la mappa dopo che è stata elaborata e si sposterà su qualsiasi cella adiacente che abbia il valore più alto. Questo si chiama arrampicata in collina . Tuttavia, la fase di elaborazione della mappa deve giàavere accesso preliminare alle informazioni sulla mappa, che qui contiene anche le posizioni di tutti i corpi AI. Pertanto, la mappa fa riferimento a IA, quindi AI fa riferimento alla mappa.

  • Computer vision e raycasting + percorso più breve Nella robotica rover e drone, questo sta diventando la norma per determinare l'estensione degli spazi che il robot naviga. Ciò consente ai robot di costruire un modello volumetrico completo del loro ambiente, proprio come faremmo con la vista o anche con il suono o il tatto (per i non vedenti o i non udenti), che il robot potrebbe quindi ridurre a un grafico topografico minimo (un po 'come un grafico di waypoint usato nei giochi con A *), su cui possono quindi essere applicati algoritmi a percorso più breve. In questo caso, mentre "visione" può fornire un indizio per l'ambiente circostante, ancora si traduce inuna ricerca grafica che alla fine fornisce il percorso verso l'obiettivo. Questo è vicino al pensiero umano: per raggiungere la cucina dalla camera da letto, devo passare attraverso il soggiorno. Il fatto che io li abbia già visti e conosca i loro spazi e i loro portali, è ciò che consente questa mossa calcolata. Questa è una topologia grafica, alla quale viene applicato un algoritmo a percorso più breve, sebbene incorporato in proteine ​​molli anziché in silicio duro.

Quindi puoi vedere che i primi due dipendono già dalla conoscenza dell'ambiente nella sua interezza. Questo, per ragioni legate al costo della valutazione di un ambiente da zero, è comune nei giochi. Chiaramente, l'ultimo è il più potente. Un robot equipaggiato in questo modo (o, ad esempio, un gioco AI che legge il buffer di profondità in ciascun fotogramma) potrebbe navigare sufficientemente in qualsiasi ambiente senza previa conoscenza di esso. Come probabilmente hai indovinato, è anche di gran lunga il più costoso dei tre approcci di cui sopra, e nei giochi in genere non possiamo permetterci di farlo su una base per AI. Certo, è molto meno costoso in 2D che in 3D.

Punti architettonici

Risulta chiaro sopra che non possiamo assumere solo un modello di accesso ai dati corretto per l'IA; la scelta dipende da ciò che stai cercando di ottenere. L'accesso GameWorlddiretto alla classe è assolutamente standard: ti fornisce semplicemente informazioni sul mondo. Essenzialmente è il tuo modello di dati, ed è a questo che servono i modelli di dati. Singleton va bene per questo.

"ottieni un elenco di tutte le entità e cerca ciò di cui hai bisogno"

Niente di ingenuo al riguardo. L'unica cosa che potrebbe essere ingenua è eseguire più iterazioni di elenco di quelle necessarie. Nel rilevamento delle collisioni, lo evitiamo utilizzando, ad esempio, quadrifre per ridurre lo spazio di ricerca. Meccanismi simili possono applicarsi all'intelligenza artificiale. E se puoi condividere lo stesso loop per fare più cose, fallo, perché i rami sono costosi.


Grazie per aver risposto. Dato che sono un principiante per gli sviluppatori di giochi, penso che per ora mi atterrò al semplice approccio "ottenere un elenco dal mondo di gioco :) :) Una domanda: nella mia domanda ho descritto la GameWorldclasse come la classe che contiene riferimenti a tutte le entità di gioco e contiene anche la maggior parte della logica "motore" importante: il circuito principale del gioco, il rilevamento delle collisioni, ecc. Fondamentalmente è la "classe principale" del gioco. La mia domanda è: questo approccio è comune nei giochi? Hai una "classe principale"? O dovrei separarlo in classi più piccole e avere una classe come un oggetto 'database di entità' può eseguire il poll?
Aviv Cohn,

@Prog Prego. Ancora una volta, non c'è nulla nei precedenti approcci AI (o altri, del resto ) che suggeriscono che il tuo "ottenere un elenco dal mondo di gioco" sia in qualche modo, forma o forma architettonicamente errato. L' architettura AI deve soddisfare i bisogni dell'IA; ma quella logica, come suggerisci, dovrebbe essere modularizzata, incapsulata (nella sua stessa classe) lontano dalla tua più ampia architettura applicativa. Sì, una volta che sorgono tali domande, i sottosistemi dovrebbero sempre essere suddivisi in moduli separati. Il tuo principio guida dovrebbe essere SRP .
Ingegnere

2

Fondamentalmente avrei 2 modi per interrogare le informazioni.

  1. quando AIState cambia perché è stata rilevata una collisione o qualsiasi cache, un riferimento a qualunque oggetto è importante. In questo modo sai di quale riferimento hai bisogno. Quando altri sistemi devono eseguire ricerche di grandi dimensioni in ogni frame, consiglierei di salvarli con un piggy back in modo da non dover eseguire più ricerche. Quindi la "collisione" rilevata con la zona che rende un "allarme" nemico inviagli un messaggio / evento che lo registra con quell'oggetto se non lo è già e cambia lo stato del gioco in uno che gli fa fare i suoi affari basandosi su essere in quel gamestate. Hai bisogno di un evento di qualche tipo che ti dice di apportare modifiche, passerei semplicemente un riferimento a qualsiasi callback che usi per fornire tali informazioni. Questo è più estensibile che dover solo avere a che fare con il giocatore. Forse vuoi che un nemico insegua un altro nemico o qualche altro oggetto. In questo modo devi solo cambiare qualunque tag tu lo identifichi.

  2. Con queste informazioni eseguirai quindi una query su un sistema di ricerca del percorso che utilizza A * o qualche altro algoritmo per fornirti un percorso o puoi usarlo con un comportamento di guida. Forse una combinazione di entrambi o altro. Fondamentalmente con la trasformazione di entrambi dovresti essere in grado di interrogare il tuo sistema di nodi o navmesh e farti dare un percorso. Il tuo mondo di gioco probabilmente ha molte altre cose oltre al pathfinding. Inoltrerei la tua richiesta solo al pathfinding. Anche raggruppare queste cose è probabilmente la cosa migliore se hai molte domande perché questo può diventare piuttosto intenso e il batch aiuterà le prestazioni.

    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;
        }
     }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.