Il modo più veloce per raggruppare le unità che possono vedersi?


12

Nel gioco 2D con cui sto lavorando, il motore di gioco è in grado di fornirmi, per ogni unità, l'elenco delle altre unità che si trovano nel suo raggio di vista.

Vorrei sapere se esiste un algoritmo stabilito per ordinare le unità in gruppi , in cui ciascun gruppo sarebbe definito da tutte quelle unità che sono "collegate" tra loro (anche attraverso gli altri).

Un esempio potrebbe aiutare a capire meglio la domanda (E = nemico, O = propria unità). Innanzitutto i dati che otterrei dal motore di gioco:

E1 can see E2, E3, O5
E2 can see E1
E3 can see E1
E4 can see O5
E5 can see O2
E6 can see E7, O9, O1
E7 can see E6
O1 can see E6
O2 can see O5, E5
O5 can see E1, E4, O2
O9 can see E6

Quindi dovrei calcolare i gruppi come segue:

G1 = E1, E2, E3, E4, E5, O2, O5
G2 = O1, O9, E6, E7

Si può presumere che esista una proprietà commutativa per il campo visivo: [se A vede B, allora B vede A].

Giusto per chiarire: ho già scritto un'implementazione ingenua che gira su ogni riga delle informazioni del motore di gioco, ma dal suo aspetto, sembra un problema abbastanza generale da essere stato studiato in profondità e avere vari algoritmi stabiliti (forse passando attraverso una struttura ad albero?). Il mio problema è che non sono riuscito a trovare un modo per descrivere il mio problema che ha restituito utili risultati di Google.

Grazie in anticipo per il vostro aiuto!


1
Penso che questa domanda sia abbastanza generale da poter ottenere risposte migliori in StackOverflow, o forse persino matematica (teoria degli insiemi?). Il mio punto non è specifico per lo sviluppo di giochi.
Tor Valamo,

1
@Tor - Probabilmente vero, ma il fatto che sappiamo che è per un gioco potrebbe consentire alle persone di elaborare risposte più specifiche per il problema.
Robert Fraser,

Penso che potresti fare alcune cose intelligenti con un giro sull'hash spaziale e una mappa di visibilità - Devo solo pensarci.
Jonathan Dickinson,

Risposte:


7

Se la tua relazione "can see" è simmetrica, in modo che "A can be B" implica che "B can be A", i gruppi che vuoi calcolare sono i componenti collegati del grafico definito dalla relazione "can be". Come altri hanno notato, esistono semplici algoritmi per il calcolo di questi, come ad esempio:

while ungrouped units remain:
    let u = arbitrary ungrouped unit
    let g = new group
    let s = temporary stack
    assign u to g
    push u onto s
    while s is not empty:
        let v = topmost unit in s
        remove v from s
        for each unit w that v can see:
            if w is ungrouped:
                assign w to g
                push w onto s
            end if
        end for
    end while
 end while

(È possibile utilizzare una coda o qualsiasi altra raccolta che implementa in modo efficiente le operazioni "aggiungi nuovo elemento" e "rimuovi e restituisce un elemento" al posto dello stack ssopra.)

Se la relazione "vedi" non è simmetrica, devi decidere se vuoi che i tuoi gruppi siano componenti fortemente o debolmente connessi. Per i componenti debolmente connessi, l'algoritmo sopra funzionerà così com'è, tranne per il fatto che la linea for each unit w that v can seedovrebbe essere sostituita con for each unit w that can see v, or that v can see. Per componenti fortemente connessi , puoi usare uno degli algoritmi ( Kosaraju , Tarjan o Gabow ) menzionati nella pagina Wikipedia collegata.

Per le relazioni non simmetriche, potresti anche voler calcolare la chiusura transitiva della relazione o dei suoi componenti fortemente connessi. Per questo, puoi usare l' algoritmo Floyd – Warshall ; vedere questa risposta su SO per (un po ') ulteriori informazioni.


Ps. Come l'articolo di Wikipedia che ho collegato alle note precedenti, potrebbe essere più efficiente aggiornare dinamicamente i gruppi quando cambia la relazione di visibilità. Non ho familiarità con gli algoritmi avanzati (?) Menzionati su Wikipedia, ma non dovrebbe essere difficile mettere insieme qualcosa che almeno batte ogni volta ricompilando i gruppi da zero.

La metà di questo è facile: se due unità in gruppi diversi acquisiscono una linea di vista tra loro, unisci i gruppi. Trattare le unità che si perdono di vista è un po 'più complicato; una soluzione semplice ma forse non ottimale è quella di rieseguire l'algoritmo di raggruppamento per le unità nel gruppo interessato ogni volta che ciò accade. Ci sono alcune ottimizzazioni che puoi apportare a questo, se i cambiamenti di visibilità si verificano una coppia di unità alla volta:

  • Se un'unità poteva vedere solo un'altra unità e la perde di vista, rimuovila dal suo gruppo precedente e assegnala a un nuovo gruppo.
  • Altrimenti, potresti iniziare da una delle unità interessate ed eseguire una ricerca A * sul grafico di visibilità (utilizzando ad esempio la distanza in linea retta come euristica) per l'altra unità. Se lo trovi, il gruppo non si è separato; in caso contrario, l'insieme di unità visitate dalla ricerca costituisce il nuovo gruppo.
  • Potresti provare a indovinare quale delle due unità ha più probabilità di appartenere alla metà più piccola del gruppo, se si divide, e iniziare la ricerca da quell'unità. Una possibilità sarebbe quella di partire sempre dall'unità che può vedere direttamente meno altre unità.

4

Quello che hai è un grafico di connettività. E in generale, il modo migliore per raggruppare i nodi collegati (cioè: caratteri) è con un algoritmo di ricerca grafico. Prima di tutto, prima di tutto, qualunque sia. Tutto quello che stai facendo è costruire un elenco di quali nodi sono raggiungibili da tutti gli altri. Finché il tuo grafico non viene indirizzato (se A è visibile a B, allora B è visibile ad A), questo funziona bene.

Potrebbero esserci alcuni algoritmi per migliorarlo in casi specifici. Ad esempio, se a volte i personaggi non si muovono (e anche il terreno non si muove, quindi i personaggi immobili rimangono visibili), puoi scegliere di non testarli di nuovo per aggiornare i loro grafici di connettività.

Ma in generale, dovrai ripetere il test di visibilità su ogni fotogramma. Le probabilità sono che sarà più lento rispetto all'attraversamento del grafico per trovare i gruppi di visibilità.


3
Solo per aggiungere il termine tecnico: quello che stai cercando di trovare sono i componenti collegati del grafico e l'algoritmo standard è: (1) metti tutti i nodi in un elenco, (2) scegli un nodo, (3) trova tutti i nodi collegati utilizzando BFS / DFS, (4) rimuovono tutti i nodi trovati dall'elenco, (5) ripetono fino a quando non rimangono più nodi.
Nathan Reed,

3

Sembra un problema di connettività grafico standard. Potrebbe esserci una sorta di algoritmo per questo, e potrebbe apparire come il seguente:

remaining units = all units
for each unit in remaining units:
    current group = create a new group
    add this unit to current group
    for each unit visible to this unit:
        if unit is in a group already:
            merge current group into that existing group
            set current group as that existing group
        else:
            remove that unit from remaining units
            add that unit to current group

Mi aspetto che sia possibile implementarlo tramite un albero, come il clustering gerarchico, ma dubito che funzionerebbe più velocemente - gli alberi tendono ad essere O (log N) mentre la maggior parte dei controlli che do sopra possono essere implementati come O (1) .


Per interesse, l'approccio del clustering gerarchico è un po 'così: per ogni unità, creare un gruppo. Quindi, per ogni coppia di unità che possono vedersi, se si trovano in gruppi diversi, unire i gruppi in uno e scartare l'altro.
Kylotan,

Questo è ciò che ho definito implementazione ingenua nel mio PO. Buono a sapersi che potrebbe non essere così male come pensavo allora! :)
mac,

Il modo in cui lo fai come un albero è utilizzare un set di unione con la compressione del percorso . Non è molto ingenuo, e in effetti è ottimale.
Peter Taylor,

2

Sono d'accordo con tutti gli altri che hanno risposto in quanto si tratta di un problema di connettività grafica, tuttavia vorrei sottolineare che ciò di cui hai bisogno qui è il grafico di triangolazione Delaunay generato da tutte le tue unità rilevanti. Quello che fa è assicurarsi che solo le unità più vicine tra loro saranno collegate nel grafico che si genera. Troverai molto difficile farlo in qualsiasi altro modo, perché gli incroci del grafico (non planarità) causeranno un collegamento errato delle unità troppo lontane l'una dall'altra all'interno del grafico.

Quanto sopra si applica solo se si utilizza uno spazio continuo (come nella maggior parte degli FPS a movimento libero); tuttavia se hai già una griglia sottostante (un grafico planare) su cui si muovono le tue unità, puoi semplicemente usarla per valutare la connettività.

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.