Le moyen le plus rapide de regrouper des unités qui peuvent se voir?

12

Dans le jeu 2D avec lequel je travaille, le moteur de jeu est en mesure de me donner, pour chaque unité, la liste des autres unités qui sont dans sa plage de vue.

Je voudrais savoir s'il existe un algorithme établi pour trier les unités en groupes , où chaque groupe serait défini par toutes ces unités qui sont «connectées» les unes aux autres (même par d'autres).

Un exemple pourrait aider à mieux comprendre la question (E = ennemi, O = propre unité). D'abord les données que j'obtiendrais du moteur de jeu:

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

Ensuite, je devrais calculer les groupes comme suit:

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

On peut supposer sans risque qu'il existe une propriété commutative pour le champ de vision: [si A voit B, alors B voit A].

Juste pour clarifier: j'ai déjà écrit une implémentation naïve qui boucle sur chaque ligne des informations sur le moteur de jeu, mais à première vue, cela semble un problème assez général pour qu'il ait été étudié en profondeur et dispose de divers algorithmes établis (peut-être en passant à travers une structure arborescente?). Mon problème est que je n'ai pas pu trouver un moyen de décrire mon problème qui a renvoyé des hits google utiles.

Merci d'avance pour votre aide!

Mac
la source
1
Je pense que cette question est suffisamment générale pour obtenir de meilleures réponses en stackoverflow, ou peut-être même en mathématiques (théorie des ensembles?). Ce n'est pas spécifique au développement de jeux, c'est mon point.
Tor Valamo
1
@Tor - Probablement vrai, mais le fait que nous sachions que c'est pour un jeu pourrait permettre aux gens d'élaborer des réponses plus spécifiques au problème.
Robert Fraser
Je pense que vous pourriez faire des trucs intelligents avec une rotation sur le hachage spatial et une carte de visibilité - je dois juste y penser.
Jonathan Dickinson

Réponses:

7

Si votre relation "peut voir" est symétrique, de sorte que "A peut voir B" implique "B peut voir A", alors les groupes que vous souhaitez calculer sont les composants connectés du graphique défini par la relation "peut voir". Comme d'autres l'ont noté, il existe des algorithmes simples pour les calculer, tels que:

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

(Une file d'attente ou toute autre collection qui implémente efficacement les opérations "ajouter un nouvel élément" et "supprimer et renvoyer un élément" peut être utilisée à la place de la pile sci-dessus.)

Si votre relation "peut voir" n'est pas symétrique, vous devez décider si vous voulez que vos groupes soient les composants fortement ou faiblement connectés. Pour les composants faiblement connectés, l'algorithme ci-dessus fonctionnera tel quel, sauf que la ligne for each unit w that v can seedoit être remplacée par for each unit w that can see v, or that v can see. Pour les composants fortement connectés , vous pouvez utiliser l'un des algorithmes ( Kosaraju , Tarjan ou Gabow ) mentionnés sur la page Wikipédia liée.

Pour les relations non symétriques, vous pouvez également vouloir calculer la fermeture transitive de la relation ou de ses composantes fortement connectées. Pour cela, vous pouvez utiliser l' algorithme Floyd – Warshall ; voir cette réponse sur SO pour (un peu) plus d'informations.


Ps. Comme l'article Wikipedia que j'ai lié aux notes ci-dessus, il peut être plus efficace de mettre à jour dynamiquement les groupes à mesure que la relation de visibilité change. Je ne suis pas familier avec les algorithmes avancés (?) Mentionnés sur Wikipédia, mais il ne devrait pas être difficile de bricoler quelque chose qui bat au moins le recalcul des groupes à partir de zéro à chaque fois.

La moitié de cela est facile: si deux unités de groupes différents acquièrent une ligne de vue entre elles, fusionnez les groupes. Gérer les unités qui se perdent de vue est un peu plus délicat; une solution simple mais peut-être pas optimale consiste à réexécuter l'algorithme de regroupement pour les unités du groupe affecté chaque fois que cela se produit. Vous pouvez y apporter quelques optimisations, si les changements de visibilité se produisent une paire d'unités à la fois:

  • Si une unité ne peut voir qu'une autre unité et la perd de vue, il suffit de la retirer de son groupe précédent et de l'affecter à un nouveau groupe.
  • Sinon, vous pourriez commencer à l'une des unités affectées et lancer une recherche A * sur le graphique de visibilité (en utilisant par exemple la distance en ligne droite comme heuristique) pour l'autre unité. Si vous le trouvez, le groupe ne s'est pas séparé; si vous ne le faites pas, l'ensemble des unités visitées constitue le nouveau groupe.
  • Vous pouvez essayer de deviner laquelle des deux unités est plus susceptible d'appartenir à la plus petite moitié du groupe, si elle se divise, et lancer la recherche à partir de cette unité. Une possibilité serait de toujours partir de l'unité qui peut voir directement moins d'autres unités.
Ilmari Karonen
la source
4

Ce que vous avez est un graphique de connectivité. Et généralement, la meilleure façon de regrouper les nœuds connectés (c'est-à-dire les caractères) est d'utiliser un algorithme de recherche de graphes. Profondeur d'abord, largeur d'abord, selon le cas. Tout ce que vous faites est de créer une liste des nœuds accessibles depuis tous les autres. Tant que votre graphique n'est pas orienté (si A est visible pour B, alors B est visible pour A), cela fonctionne très bien.

Il peut y avoir des algorithmes pour améliorer cela pour des cas spécifiques. Par exemple, si parfois les personnages ne bougent pas (et le terrain ne bouge pas aussi, de sorte que les personnages immobiles restent visibles), vous pouvez choisir de ne pas les tester à nouveau pour mettre à jour leurs graphiques de connectivité.

Mais en général, vous devrez retester la visibilité à chaque image. Les chances sont que cela va être plus lent que le parcours du graphique pour trouver les groupes de visibilité.

Nicol Bolas
la source
3
Juste pour ajouter le terme technique: ce que vous essayez de trouver, ce sont les composants connectés du graphique, et l'algorithme standard est: (1) mettre tous les nœuds dans une liste, (2) choisir un nœud, (3) trouver tous les nœuds connectés à l'aide de BFS / DFS, (4) supprimez tous les nœuds que vous avez trouvés de la liste, (5) répétez jusqu'à ce qu'il ne reste plus de nœuds.
Nathan Reed
3

Cela ressemble à un problème de connectivité graphique standard. Il peut y avoir une sorte d'algorithme pour cela, et il pourrait ressembler à ceci:

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

Je m'attends à ce qu'il soit possible de l'implémenter via un arbre, comme le clustering hiérarchique, mais je doute que cela fonctionnerait plus rapidement - les arbres ont tendance à être O (log N) alors que la plupart des vérifications que je donne ci-dessus peuvent être implémentées comme O (1) .

Kylotan
la source
Par intérêt, l'approche de clustering hiérarchique est un peu comme ceci: Pour chaque unité, créez un groupe. Ensuite, pour chaque paire d'unités qui peuvent se voir, si elles sont dans des groupes différents, fusionnez les groupes en un et jetez l'autre.
Kylotan
C'est ce que j'ai appelé une mise en œuvre naïve dans mon PO. Bon à savoir que ce n'est peut-être pas aussi mauvais que je le pensais alors! :)
mac
La façon dont vous le faites sous forme d'arborescence consiste à utiliser un ensemble d'unions avec compression de chemin . Ce n'est pas très naïf, et en fait, c'est optimal.
Peter Taylor
2

Je suis d'accord avec tous ceux qui ont répondu qu'il s'agissait d'un problème de connectivité graphique, mais permettez-moi de souligner que ce dont vous avez besoin ici est le graphique de triangulation Delaunay généré à partir de toutes vos unités pertinentes. Cela garantit que seules les unités les plus proches les unes des autres seront connectées dans le graphique que vous générez. Il vous sera très difficile de procéder de cette manière, car les croisements de graphiques (non-planarité) entraîneront des unités trop éloignées les unes des autres pour être incorrectement connectées dans le graphique.

Ce qui précède ne s'applique que si vous utilisez un espace continu (comme dans la plupart des FPS à mouvement libre); cependant, si vous avez déjà une grille sous-jacente (un graphique planaire) sur laquelle vos unités se déplacent, vous pouvez simplement l'utiliser pour évaluer la connectivité à la place.

Ingénieur
la source