Come implementare un nemico intelligente in uno sparatutto?


13

Immagina uno sparatutto molto semplice, qualcosa che tutti sappiamo:

shoot-em-up 1

Sei il giocatore (verde). Il tuo movimento è limitato Xall'asse. Il nostro nemico (o nemici) è nella parte superiore dello schermo, il suo movimento è anche limitato Xall'asse. Il giocatore spara proiettili (gialli) contro il nemico.

Vorrei implementare un'intelligenza artificiale per il nemico che dovrebbe essere davvero bravo a evitare i proiettili dei giocatori. La mia prima idea è stata quella di dividere lo schermo in sezioni discrete e assegnare loro dei pesi:

shoot-em-up ponderato

Esistono due pesi: il "peso proiettile" (grigio) è il pericolo imposto da un proiettile. Più il proiettile è vicino al nemico, maggiore è il "peso del proiettile" ( 0..1, dove 1 è il pericolo più elevato). Le corsie senza proiettile hanno un peso di 0. Il secondo peso è il "peso-distanza" (verde lime). Per ogni corsia aggiungo il 0.2costo di movimento (questo valore è un po 'arbitrario ora e potrebbe essere modificato).

Quindi aggiungo semplicemente i pesi (bianco) e vado sulla corsia con il peso più basso (rosso). Ma questo approccio ha un difetto evidente, perché può facilmente mancare i minimi locali poiché il posto ottimale dove andare sarebbe semplicemente tra due proiettili in arrivo (come indicato dalla freccia bianca).

Quindi ecco cosa sto cercando:

sparare-su-distruzione totale

  • Dovrebbe trovare un modo per superare la tempesta di proiettili, anche quando non c'è posto che non imponga la minaccia di un proiettile.
  • Il nemico può schivare in modo affidabile i proiettili scegliendo una soluzione ottimale (o quasi ottimale).
  • L'algoritmo dovrebbe essere in grado di tenere conto della velocità di movimento dei proiettili (poiché potrebbero spostarsi con velocità diverse).
  • Modi per modificare l'algoritmo in modo da poter applicare diversi livelli di difficoltà (stupidi ai nemici super-intelligenti).
  • L'algoritmo dovrebbe consentire obiettivi diversi, poiché il nemico non vuole solo sfuggire ai proiettili, ma dovrebbe anche essere in grado di sparare al giocatore. Ciò significa che le posizioni in cui il nemico può sparare al giocatore dovrebbero essere preferite quando schivano i proiettili.

Quindi come affronteresti questo? Contrariamente ad altri giochi di questo genere, mi piacerebbe avere solo pochi nemici, ma molto "abili" invece di masse di nemici stupidi.


2
Hai mai pensato di usare qualcosa come i comportamenti di guida? Ce n'è uno per evitare ostacoli specificamente: red3d.com/cwr/steer/Obstacle.html
Tetrade

@Tetrad Ho pensato ai comportamenti dello sterzo .. anche perché possono essere commutati bene, come "provare a sparare al giocatore" e quando il pericolo è avanti passare a "eludere". Temo che una versione 1D (questo è quello con cui ho a che fare sostanzialmente) di evade sarà troppo stupida per prendere buone decisioni. Potrei sbagliarmi però.
Bummzack,

Risposte:


8

Penso che la tua idea di base sia valida, ma non è analoga. È necessario un campo di valore analogico che attraversa lo schermo. Quindi, gradiente di diffusione 1D, da cui è possibile derivare un valore in un punto esatto su quella linea, al volo. I gradienti di diffusione sono economici e possono essere utilizzati da più nemici contemporaneamente, poiché descrivono l'ambiente e non la visione dell'entità (un po 'come l'illuminazione della radiazione) - probabilmente perché hai optato per l'approccio che hai nella tua domanda . Questo gradiente dovrebbe essere relativamente regolare, in modo da evocare movimenti organici dal nemico, e ovviamente si aggiorna come fa il tuo gamestate. Forse media mobile ?

Il gradiente dovrebbe combinare:

  • Prossimità
  • Velocità
  • Larghezza del proiettile
  • (facoltativo) Posizione degli obiettivi (giocatori)

Per evitare, dobbiamo essere in grado di trovare una soluzione accuratamente ogni volta che esiste una soluzione . Tale è il caso ogni volta che c'è un divario abbastanza piccolo da consentire al nemico di schivare. Cioè, puoi solo fare quello che puoi fare, quindi l'approccio gradiente non funzionerà peggio di qualsiasi altro approccio in questo senso, direi.

Il gradiente di diffusione dovrebbe spingere il nemico verso optima locale (essendo i picchi nel grafico) con meno imperativo da spostare, più ci avviciniamo al minimo locale, quindi un effetto di ritorno decrescente sulla schivata. Questo apre le porte a processi decisionali più intelligenti su quando il nemico ha una buona apertura al fuoco.

Se la necessità di sparare è maggiore della necessità di muoversi, allora fallo; il tuo codice può anche determinare ciò in base alla differenza. Puoi implementarlo come parte del grafico di base, nel qual caso la posizione del giocatore riduce i valori circostanti nel grafico (supponendo che i nemici gravitino sul punto più basso), che combina tutto il processo decisionale in un grafico, oppure puoi mantenere il " desiderio di sparare "grafico separato dal grafico principale" desiderio di schivare ", che ti offrirà un controllo più diretto.

IRL, non mi preoccuperei di schivare un proiettile finché non si trova a una distanza che conosco, alla mia massima velocità di schivata, sta iniziando a diventare difficile da evitare. Esperienza di prima mano dal lancio di pietre da ragazzo. Un proiettile a distanza x che viaggia a velocità y ha la stessa classificazione di pericolo di un proiettile a distanza 2x che viaggia a 2y. Quindi questo deve essere preso in considerazione correttamente.

I modi per modificare l'algoritmo per la difficoltà del nemico includono

  • introducendo una latenza sugli aggiornamenti del grafico (Born Too Slow)
  • introducendo nel grafico innatezze casuali e localizzate
  • semplicemente il fatto che i nemici non obbediscano a ciò che il grafico dice loro, su una sequenza di aggiornamenti (diciamo da 1 a 10 fotogrammi) a causa della pura pigrizia dell'IA.

Ancora una volta, puoi implementare tutti i fattori in un grafico (meno controllo e meno utile per più nemici) o in diversi grafici che guardi insieme per ottenere i risultati (più modulare, probabilmente funziona meglio per più nemici). È difficile essere più specifici, poiché ci sono molte direzioni in cui puoi adottare questo approccio.


1
Caro Nick. Sono andato avanti e ho implementato una piccola versione di prova di quel comportamento in flash e sono molto contento del risultato (i proiettili vengono generati casualmente). Attualmente sto usando 3 gradienti, uno per minaccia, costo di movimento e uno statico per i bordi (in modo che i bordi dello schermo siano meno desiderabili). Sono visualizzati nell'applicazione flash. Sento che con alcune modifiche posso ottenere risultati molto buoni e anche considerare altri pesi come le posizioni di tiro. Grazie mille amico.
Bummzack,

1
Ehi @bummzack, è un compagno di piacere, è una piccola demo interessante! La tua visione del problema era nuova per me e sembrava interessante - sono felice di vedere che funziona! Sono contento di aiutarti, e grazie per la condivisione.
Ingegnere

7

Questo può essere considerato un problema di tracciamento. Invece di pensare a come il malvagio evita i proiettili, l'imaging dei proiettili è statico e il malvagio deve attraversarli fino in fondo allo schermo.

    E    
B  B**B
  B***B  B
 B***B   B
B**B** B 
 B**B**BB
B*****B B
      P

E = nemico
B = proiettile
P = giocatore
* = opzioni percorso verso la parte inferiore dello schermo

Una volta che il cattivo ha tracciato un percorso di successo, deve solo fare il passo successivo ogni volta. Probabilmente ci sono già alcuni buoni algoritmi in giro per trovare percorsi come questo. Se il cattivo si muove alla stessa velocità dei proiettili potrebbe essere un algoritmo di esempio;

Inizia dal baddy e segna le posizioni sicure negli spazi vuoti in basso a sinistra, direttamente in basso e in basso a destra. Quindi considera ogni spazio sicuro che hai appena creato e ripeti. Se in qualsiasi momento trovi che non ci sono spazi sicuri sottostanti, contrassegna lo spazio come non sicuro e torna indietro.


1
Approccio interessante Temo che questo sia un po 'costoso, poiché l'ambiente cambia così velocemente ed è difficile usare algoritmi di ricerca dei percorsi comuni poiché funzionano meglio con "mappe" discrete.
Bummzack,

Non dovrebbe essere troppo costoso, non dimenticare che devi solo calcolare la riga inferiore ogni turno, non devi ricalcolare il tutto.
Qwerky,

L'ambiente di @bummzack non è "veloce", almeno in termini di computer. Per lo sviluppatore di giochi, dovresti capire che quasi tutti i giochi sono basati su step, si tratta solo delle dimensioni di quel step. Ma, ad ogni passo, è possibile effettuare calcoli, quindi la soltuion di Qwerky è la cosa di cui hai bisogno.
Deele,

In realtà, sono d'accordo con @bummzack. Sebbene questo approccio sia logicamente solido, sarà più costoso dell'approccio 1D proposto nella domanda. Può essere un'ottimizzazione prematura, ma trovo che questo approccio sia molto più elegante. Vedi la mia risposta per un'elaborazione al riguardo.
Ingegnere

2
Questa è probabilmente la risposta corretta , ma non esattamente realistica . Nel tempo necessario al nemico per elaborare un percorso, il campo può avere un altro set di proiettili che invalida completamente il percorso, il che significa che ricalcolare il percorso sarebbe un must. E il ricalcolo è una cagna . Inoltre, la tua idea di backpropagating fino a renderla sicura lo rende ancora più costoso! @Nick Wiggill Non credo sia un'ottimizzazione prematura, solo una buona previsione per assicurarti di non prenderti a calci nel cavallo.
Ray Dey,
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.