Quadruple arbre / collision basée sur la grille - mettre la logique en action

13

Tout d'abord, je n'écris ma propre logique de jeu que depuis peu de temps, donc je m'excuse si cela peut sembler simple.

J'ai beaucoup lu sur les arbres quadruples et la détection de collision basée sur une grille. Je comprends la logique - fondamentalement, ne vérifie pas la collision à moins que les objets ne soient proches à la base. Mais il n'est jamais mentionné comment l'exécuter réellement.

J'ai quelques méthodes possibles dans ma tête, mais je ne sais pas laquelle est la meilleure

Test de collision général - pas d'optimisation

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }

stocker les voisins (méthode 1) Mais que faire si nous voulons optimiser les collisions pour ne vérifier que les objets qui sont proches. Passons-nous toujours en revue tous les objets, ou créons-nous un tableau avec des objets proches à vérifier?

var objects:Array = new Array();
    var neighbours:Array = new Array();

    for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){

               neighbours.push(objectA, objectB);
              }
        }

    }

    //somewhere else
    for(i:int = 0; i < neighbours.length; i++){
        //only check neighbours

        for(j:int = i + 1; j < neighbours.length; j++){

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }
    }

boucle tous les objets mais vérifie seulement les voisins pour la collision (méthode 3) L'autre possibilité est que nous bouclons tout, mais vérifions si les objets sont proches avant de tester la collision.

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){
               //they are near - check collision!
               if(objectA.collidesWith(objectB){

               //handle collision logic
              }
            }
        }

    }

Stocker des objets dans les données de tuiles (méthode 3) L' utilisation d'un système basé sur les tuiles permet une autre option; Stockez les objets qui se trouvent sur une tuile spécifique dans les données de tuiles elles-mêmes. Vérifiez pour voir sur quelle tuile l'objet est les tuiles environnantes contiennent tous les objets avec lesquels il pourrait entrer en collision:

var ObjectA;

for(var i:int = 0; i < 4; i ++){
//check 4 surrounding tiles from object A

   if(Object.currentTile + surroundingTile[i] CONTAINS collidable object){
   //check collision!
    if(objectA.collidesWith(surroundingTile.object){

    //handle collision logic
     }

}
}

J'essaie toujours de regarder le monde réel comme exemple. Si je voulais comparer des articles de la même couleur, il semblerait illogique de vérifier chaque article entier même s'ils ne correspondent pas à la couleur (méthode 2, vérifiez chaque article). Je collecterais probablement les articles de la même couleur (objets qui sont proches les uns des autres) et les vérifierais (méthode 1), au lieu de tout vérifier.

Ce n'est pas une comparaison appropriée, car les éléments de la vérification des collisions se déplacent constamment, de sorte que la commande est mélangée. C'est ce qui me déroute.

Serait-il plus efficace de vérifier chaque élément, supprimant ainsi la contrainte de continuer à générer un tableau de voisins.

Ou est-il plus efficace de trouver des voisins sans avoir à parcourir autant d'objets pour vérifier la collision?

Continuer à changer les données sur chaque tuile semble également très intensif, donc je ne sais pas si c'est une bonne idée ..

J'ai pensé à un jeu de tower defense où la tour doit détecter des objets si des objets sont à portée avant de lui tirer dessus. Et il semble juste stupide de vérifier tous les éléments alors qu'à certains moments, il n'y aura aucun objet à proximité.

Je m'excuse pour le long post, j'ai toujours du mal à m'expliquer!

omgnoseat
la source
Quelle langue est-ce? Javascript?
Kyle C
2
Actionscript 3, mais le langage n'est pas pertinent. Je veux juste savoir quelle est la meilleure façon de gérer et de structurer cela :)
omgnoseat

Réponses:

8

Vous devez réduire le nombre de vérifications de collision réelles. Ouais c'est évident je sais. Permet donc d'élaborer sur ce point:

Votre premier algorithme de vérification des collisions sans aucune optimisation: combien de vérifications fera-t-il en une seule fois? Si n est le nombre d'objets, ce sera autour de n * (n-1) contrôles. Pour beaucoup d'objets (> 100), ce sera horriblement lent.

La méthode de vérification de votre voisin ne sera pas beaucoup plus rapide. Si vos objets ne sont pas statiques et se déplacent beaucoup, vous devrez créer cette liste de voisins pour chaque objet à chaque fois dans votre boucle de jeu. C'est en fait pire que le premier algorithme puisque vous faites n * (n-1) pour construire la liste des voisins, puis vérifiez chaque objet s'il entre en collision avec l'un de ses voisins.

Vous devez partitionner votre espace de jeu. Disons que votre espace de jeu fait 400x400 pixels de large. Vous pouvez le partitionner en 4 sous-espaces de 200 x 200 et vérifier chaque objet dans lequel des sous-espaces il appartient (un objet peut se trouver dans plus d'un sous-espace). Vous n'aurez alors qu'à vérifier si les objets de chaque sous-espace entrent en collision avec d'autres objets du même sous-espace.

Le coût d'exécution sera donc: n pour la construction de la liste des 4 sous-espaces + (n / 4) * ((n-1) / 4), ce qui est bien meilleur que le coût d'exécution précédent. Cela peut être réduit davantage en réduisant les sous-espaces (par exemple 50x50).

Donc, notre algorithme ressemble maintenant à ceci:

for each (object in objects)
  check into which subspace the object belongs

for each (subspace in subspaces)
  for each (object in subspace)
    check object for collision with other objects in same subspace

Ceci est quelque peu similaire à votre idée de données de tuile. Mais les sous-espaces n'ont pas besoin d'avoir la même taille que les tuiles.

Nous pouvons faire une dernière étape pour l'optimiser davantage en utilisant un quadtree. Soit k le nombre de sous-espaces. Pour construire les listes d'objets d'espaces, nous effectuons des vérifications k * n, ce qui entraîne de nombreuses vérifications si votre monde de jeu devient grand.

Pour réduire ce coût, nous utilisons un quadtree. Un quadtree est une autre façon de partitionner notre espace de jeu. Au lieu de diviser notre espace 400x400 en 64 sous-espaces 50x50 et de vérifier pour chaque objet dans lequel des 64 sous-espaces il se trouve actuellement, l'espace de jeu est divisé en 4 sous-espaces de la moitié de la taille de l'espace de jeu (200x200), qui à leur tour sont divisés en des sous-espaces plus petits (100x100), qui à leur tour sont divisés à nouveau en sous-espaces 50x50. Ceci est notre quadtree.

Maintenant, si nous voulons savoir à quel sous-espace 50x50 appartient un objet, nous vérifions à quel sous-espace 200x200 il appartient. Ensuite, nous allons un niveau plus loin dans notre quadtree et vérifions les 4 sous-espaces 100x100 qui sont à l'intérieur du sous-espace 200x200 que nous venons de trouver. Ceci est répété jusqu'à ce que nous sachions dans quel sous-espace 50x50 appartient l'objet. Alors, combien de contrôles étaient maintenant nécessaires? 4 pour chaque niveau du quadtree (rappelez-vous qu'un objet peut être sur le bord de deux sous-espaces). Nous avons donc besoin de 4 * 3 vérifications pour affecter un objet au sous-espace 50x50 correct. Beaucoup mieux que 64 chèques.

Donc, notre algorithme quadtree a besoin de 4 * 3 * n vérifications pour construire les listes de sous-espaces, puis quelque chose comme k * (n / k) vérifie pour vérifier les collisions de chaque objet.

Stephen
la source
C'est un avertissement très clair et compréhensible, merci beaucoup! Comment est-il géré dans quel sous-espace se trouve un objet? Le sous-espace est-il manipulé en tant qu'objet, où l'objet sur celui-ci est enregistré en tant que tableaux et vérifié les uns par rapport aux autres? Ou vérifiez-vous simplement de quel sous-espace il s'agit en vérifiant les X et Y à l'intérieur de la boucle principale? On dirait que construire et changer des tableaux tout le temps est assez inefficace. Je veux donc être sûr d'utiliser la méthode appropriée :) Je peux également voir un problème se produire lorsque les objets varient en taille. Je suppose que le plus petit sous-espace devrait être à peu près aussi grand que le plus grand objet?
omgnoseat
Je suis heureux de vous aider :) Un sous-espace serait un objet qui maintient les 4 sous-espaces qu'il contient. Le plus petit sous-espace est différent car il ne contient plus de sous-espaces, mais une liste de vos objets. Compte tenu du coût de la mise à jour: l'arborescence du sous-espace est construite une fois au début du jeu. Dans chaque boucle de la boucle principale, les listes d'objets dans les plus petits sous-espaces sont effacées et remplies à nouveau. Donc, seule la liste d'ajout / suppression est nécessaire. Aucun tableau ne doit être modifié. Le plus petit sous-espace doit être suffisamment grand pour contenir plusieurs objets de jeu. La taille dépend de votre jeu et peut être modifiée ultérieurement.
Stephen
Merci, j'ai oublié que nous allons également vérifier les collisions entre les objets réels dans le plus petit sous-espace, il est logique qu'il devrait pouvoir contenir plusieurs objets maintenant. J'ai commencé un test et ça se passe bien. Le plus gros problème que j'ai est de détecter à quel sous-espace appartient un objet. Je continue de parcourir les valeurs X et Y des sous-espaces childs, est-ce une méthode appropriée?
omgnoseat
Comment gérez-vous les objets qui s'étendent dans plus d'un sous-espace (c'est-à-dire dont l'emplacement est sur ou près d'une frontière)?
mghicks
Simple: non. Les objets peuvent faire partie de plusieurs sous-espaces, s'ils se trouvent à la limite de chaque sous-espace. Ainsi, un objet peut faire partie de jusqu'à 4 sous-espaces. Mais c'est la minorité.
Stephen