Gestisci un gran numero di attori indipendenti in tempo reale


8

Sto lavorando a un gioco di strategia in tempo reale su larga scala - idealmente con migliaia di unità attive contemporaneamente - ma ho problemi a gestire tutte le unità contemporaneamente senza che diventi sorprendentemente lento. Il problema è che ci vuole un po 'di tempo per aggiornare le posizioni e gli stati di tutto ogni volta che si passa. Conosci qualche modello / metodologia / suggerimento per mitigarlo?


1
anche se la tua domanda può sembrare molto diversa, puoi usare lo stesso algoritmo spiegato qui . non è necessario utilizzare l'elaborazione parallela in quell'algoritmo, con solo un singolo otterrai un aumento delle prestazioni.
Ali1S232,

Parallelizzare l'aggiornamento non sarà utile se, ad esempio, hai due core e l'aggiornamento di metà di essi richiede già troppo tempo, ma se hai più core disponibili e non li stai già utilizzando, dovrebbe aiutare. Idealmente, vorrai una soluzione che non richieda l'aggiornamento di ogni singola unità ogni singolo passaggio temporale o un modo per semplificare l'aggiornamento in modo da poterlo fare. Quanto è grande questo passaggio? È indipendente dai frame di disegno, giusto?
Blecki,

4
La prima cosa che puoi fare è riconoscere che la tua velocità non ha nulla a che fare con la quantità di attori indipendenti. Ha a che fare con quello che stanno facendo quegli attori . Posso avere centomila attori indipendenti che aggiornano> 60 fps se tutto ciò che stanno facendo è if not dead position += 1o un attore che aggiorna <1 fps se sta andando in un ciclo infinito. Alcuni dei tuoi algoritmi - alcune parti di ciò che stanno facendo queste unità - sono semplicemente troppo costosi nel modo in cui li stai facendo e questo è tutto. Probabilmente ci sono molte diverse possibili cause e molte diverse possibili strategie per ognuna.
doppelgreener,

Risposte:


4

Ci sono due cose distinte da considerare quando si misura e si ottimizzano le prestazioni di un sistema di entità così ampio.

A basso livello, hai la rappresentazione fisica delle tue entità che tende a ridursi all'utilizzo di layout di archiviazione efficienti come SoA (strutture di array) per ridurre i costi di iterazione e aggiornamento di tutte le entità attive.

Al livello superiore, hai la logica del processo decisionale, la logica generale del gioco, l'intelligenza artificiale e il pathfinding. Queste sono tutte attività che hanno in comune il fatto che non devono essere eseguite alla stessa velocità di aggiornamento del rendering.

Dato che si otterrebbe un tempo di frame irregolare se si adotta l'approccio ingenuo di svolgere tali compiti ogni N frame, tende ad essere utile ammortizzare il costo su più frame.

Se l'attività ha natura incrementale, è possibile eseguire parte dell'algoritmo in ogni frame e utilizzare risultati parziali negli aggiornamenti.

Se l'attività è in gran parte monolitica ma separabile per entità, è possibile eseguire tale attività per un sottoinsieme delle entità di gioco per fotogramma, ruotando tra loro in modo circolare. Questo ha il vantaggio di ridurre la complessità di cose come l'individuazione di percorsi e l'intelligenza artificiale, dato che non tutti hanno tentato di agire contemporaneamente.

Nel RTS tattico su larga scala su cui ho lavorato, ci siamo concentrati sull'avere strutture dati robuste per interrogare la rappresentazione di basso livello negli algoritmi di alto livello, per trovare i vicini di entità di gioco. Il processo di aggiornamento di basso livello ha agito sugli intenti forniti dalla simulazione di alto livello ad aggiornamento lento e alla fine si è ridotto a una simulazione di particelle a basso costo, scalando bene a migliaia.


+1. Esattamente i miei pensieri, in particolare riducendo la logica a gruppi gestiti che vengono elaborati solo ogni pochi cicli, con tutti questi gruppi potenzialmente sfalsati per un'elaborazione uniforme.
Ingegnere

1

per quanto posso ricordare, avrai sempre meno di 10.000 unità a partita. non esiste un gioco che posso ricordare con più di quel numero, anche se la Terra dell'Impero potrebbe arrivare fino a 14000 attraverso un hack ma nessuno potrà mai arrivare a quel punto. quindi avere un array statico di 10.000 oggetti sembra essere più del necessario.

come sai che iterare oltre 10000 oggetti non è un grosso problema, ma potrebbe richiedere molto tempo se l'algoritmo funziona più lentamente di O (n). ad esempio, se si tenta di controllare ogni due oggetti per il rilevamento delle collisioni, occorrerà O (n ^ 2) tempo, il che significa molto tempo. quindi devi rompere i tuoi algoritmi in qualche modo. rivediamo un algoritmo di esempio per ogni cosa che mi viene in mente in questo momento:

  1. rilevamento delle collisioni: per ogni algoritmo di rilevazione delle collisioni devi controllare ogni due oggetti ma puoi eliminare alcuni controlli iniziando i loop. come ho suggerito nei commenti è possibile utilizzare lo stesso algoritmo fornito per questa domanda . non è necessario utilizzare più thread o altro, anche con un thread e 4 regioni ridurrai i tuoi controlli da n * (n-1) a 4 * (n / 4) ((n-1) / 4), e ottimizzando il numero di regioni è possibile ottenere risultati ancora migliori. Penso che usando le migliori regioni numeriche puoi persino arrivare a O (n log (n)).

  2. devi generare il percorso per ogni oggetto in movimento. il solito sistema che ho visto finora è una cosa molto semplice: ogni volta che il giocatore comanda alle unità di spostarsi da qualche parte il computer calcola il suo percorso, dopodiché in ogni ciclo se l'oggetto può muoversi si muove se non ci riesce salta quel ciclo. non c'è niente di speciale. anche se puoi anche modificare gli algoritmi forniti qui per ridurre le chiamate di ricerca del percorso e avere la ricerca del percorso in tempo reale per ciascun gruppo di unità, ma non è necessario.

  3. devi controllare se qualche proiettile o bomba o qualcosa di simile colpisce una delle unità: puoi usare le stesse regioni che hai creato per il rilevamento delle collisioni qui.

  4. per selezionare le unità puoi anche usare le stesse regioni.

in generale, suggerirei di utilizzare un array statico (o un array dinamico con dimensioni riservate) di 10.000 o 20.000 al massimo. quindi usa qualcosa di circa 10 o 15 loop ciascuno ripetendo tutte le unità. questo è un vero array di grandi dimensioni contenente tutte le unità di tutti i giocatori. quindi ogni indice contiene sia dati sul proprietario dell'unità sia sul tipo di unità. puoi anche creare altri array per ciascun giocatore. in ogni indice di questi array secondari dovrai solo memorizzare i puntatori agli oggetti nell'array principale.

se hai altre domande inserisci commenti per aggiungerli alla mia risposta.


Ricordo di aver avuto oltre 40.000 in Empires Dawn of the Modern World :). Peccato che il renderer potesse mantenere circa un 1000 sullo schermo anche se prima che iniziassero a scomparire, ma il resto del gioco ha funzionato bene :).
deceleratedcaviar

@daniel: non è un grosso problema, i generali non hanno avuto limiti al conteggio delle unità. Stavo solo dando una certa misura del buon numero di unità su cui basare i miei calcoli matematici. 40000 e 10000 non sono molto diversi tra loro.
Ali1S232
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.