Élimination efficace des objets hors écran sur une carte 2D descendante

8

Je sais que l'efficacité est la clé de la programmation de jeux et j'ai déjà eu quelques expériences avec le rendu d'une "carte" mais probablement pas de la meilleure des manières.

Pour un jeu 2D TopDown: (restituez simplement les textures / tuiles du monde, rien d'autre)

Disons que vous avez une carte de 1000x1000 (tuiles ou autre). Si la tuile n'est pas dans la vue de la caméra, elle ne devrait pas être rendue - c'est aussi simple que cela. Pas besoin de rendre une tuile qui ne sera pas vue. Mais puisque vous avez 1000x1000 objets dans votre carte, ou peut-être moins, vous ne voudrez probablement pas parcourir toutes les 1000 * 1000 tuiles juste pour voir si elles sont supposées être rendues ou non.

Question: Quelle est la meilleure façon de mettre en œuvre cette efficacité? Pour qu'il "rapidement / plus rapidement" puisse déterminer quelles tuiles sont supposées être rendues?

De plus, je ne construis pas mon jeu autour de tuiles rendues avec un SpriteBatch donc il n'y a pas de rectangles, les formes peuvent être de tailles différentes et avoir plusieurs points, disons un objet courbe de 10 points et une texture à l'intérieur de cette forme;

Question: Comment déterminez-vous si ce type d'objets est "à l'intérieur" de la vue de la caméra?

C'est facile avec un rectangle 48x48, il suffit de voir si la largeur X + ou la hauteur Y + est dans la vue de la caméra. Différent avec plusieurs points.

Autrement dit, comment gérer efficacement le code et les données pour ne pas avoir à parcourir / boucler un million d'objets en même temps.

Deukalion
la source

Réponses:

6

En ce qui concerne les objets complexes , je pense que la meilleure façon est simplement de les réduire aux rectangles environnants et de vérifier si ce rectangle est à l'intérieur de la fenêtre. Même si vous restituez une texture qui n'est pas réellement visible (en raison de sa forme), elle sera probablement plus rapide que de faire un algorithme de détection plus complexe.

Quant à la gestion efficace de grandes cartes, vous devez subdiviser votre carte à plus grande échelle, par exemple 10x10. Ensuite, vous vérifiez l'intersection de votre fenêtre. Dans le pire des cas, il frappe 4 ces «régions», ce qui donnera (100x100) * 4 = 40K objets. Ceci est un exemple simplifié. Pour une utilisation réelle, vous devriez considérer la structure Quadtree qui est particulièrement efficace pour de telles subdivisions et la détection de collision (le contrôle de la visibilité de la fenêtre est essentiellement un contrôle de la collision entre la fenêtre et le sprite).

Petr Abdulin
la source
L'utilisation d'un Quadtree pour les tuiles de carte est un peu exagéré ... car vous pouvez simplement calculer les index appropriés des tuiles qui doivent être rendues. Et d'ailleurs, les régions sont plus simples, et je recommanderais de les utiliser pour le premier cycle d'optimisations. Cela aidera à comprendre et à utiliser les arbres quadruples plus tard :) +1.
Liosan
@Liosan, il n'est pas clair à la question si ces «tuiles» sont de la même taille, sinon la solution serait assez triviale, bien sûr.
Petr Abdulin
vous avez raison, Deukalion a même écrit un commentaire à une réponse différente en disant: «Et une tuile n'a pas toujours la même taille exacte».
Liosan
QuadTree fonctionne-t-il même avec des tailles de région autres que celles exactes? Chaque région ne doit pas avoir la taille d'un rectangle car l'objet à l'intérieur du rectangle ne l'est pas et ne fait donc pas de rectangle. Donc, ce ne sera PAS une grille de 1024x1024 pixels qui sera rendue, sa forme pourrait être très orthodoxe.
Deukalion
Eh bien, je suppose que non (je n'ai pas vraiment utilisé de quadtrees moi-même), mais cela n'a pas vraiment d'importance si vous pouvez tout mettre dans la région du rectangle environnant. Quoi qu'il en soit, si Quadtree ne convient pas à votre tâche pour une raison quelconque, vous devez très probablement utiliser une approche similaire.
Petr Abdulin
3

Lorsque vous avez de nombreux objets mobiles, vous devez les stocker par leurs coordonnées dans une structure arborescente multidimensionnelle. De cette façon, vous pouvez obtenir efficacement une liste de tous les objets qui se trouvent à l'intérieur d'un rectangle donné. Vous pouvez même les classer par leurs coordonnées x ou y, ce qui est important pour l'ordre de dessin lorsque les images-objets se chevauchent.

Cela sera également très utile pour la détection des collisions.

Voir l'article de wikipedia sur les arbres kd pour plus de détails.

Lorsque les arbres 2D sont trop compliqués pour vous, il existe également une alternative plus simple mais pas beaucoup moins efficace: stocker les objets en tant qu'enfants des tuiles. Lorsque vous déplacez un objet, vous le supprimez de la liste d'objets de son ancienne tuile et le placez dans la liste d'objets de la nouvelle. Lorsque vous dessinez les objets, vous parcourez à nouveau les tuiles de la fenêtre et récupérez leurs objets. Ensuite, vous les triez tous par des coordonnées y et les dessinez.

Philipp
la source
1

Je ne sais pas si c'est la meilleure façon, mais voici comment j'apprends à le faire:

vous avez un tableau bidimensionnel de "tuiles"

public Tile tilemap[][];

et vous décidez de la position de la "caméra" avec un Vector2, vous ne rendrez que ce qui est à l'intérieur de la scène, le grand rectangle est ce que vous pouvez voir à l'écran, inutile de dessiner le reste de la scène.

Maintenant, vous devez obtenir les décalages, en supposant que vous souhaitez que votre caméra soit au centre de la scène:

offsetX = (graphics().width() / 2 - Math.round(cam.Position().X));
offsetX = Math.min(offsetX, 0);
offsetX = Math.max(offsetX, graphics().width() / 2 - mapWidth);
offsetY = (graphics().height()) / 2 - Math.round(cam.getPosition().Y);
offsetY = Math.min(offsetY, 0);
offsetY = Math.max((graphics().height() / 2 - mapHeight), offsetY);

maintenant, dans quelle partie du tableau les tuiles visibles commencent et finissent?

firstTileX = pixelsToTiles(-offsetX);

lastTileX = firstTileX + pixelsToTiles(graphics().width());

firstTileY = pixelsToTiles(-offsetY);

lastTileY = firstTileY + pixelsToTiles(graphics().height());

int pixelsToTiles(int pixels) {
    return (int) Math.floor((float) pixels / Tile.getHeight());
}

et dans votre méthode de dessin, il vous suffit de parcourir la partie visible du tableau:

   for (int x = firstTileX; x < lastTileX; x++) {
        for (int y = firstTileY; y < lastTileY; y++) {
              Vector2 position = new Vector2(tilesToPixelsX(x) + offsetX,
                        tilesToPixelsY(y) + offsetY);
              tilemap[x][y].Draw(surf, position);
        }
    }
riktothepast
la source
1
Oui, mais la tuile était un exemple pour simplifier les choses. J'ai déjà écrit que je comprends le processus consistant à déterminer si un objet est déjà en "vue" avec un rectangle / titre en forme, pas avec des formes plus avancées qui ont plusieurs points. De plus, je cherchais quelque chose qui me fait "ne pas" parcourir toutes les tuiles lors de chaque méthode Update () dans XNA. Si j'ai une carte "LARGE", avec environ 10000 objets (formes de 3 points et plus), ce n'est pas la façon de le faire. Chaque mise à jour, je dois exécuter une boucle de 10000 mises à jour et autant de calculs. Je n'utilise pas de tuiles et ce n'est pas efficace; J'ai été là.
Deukalion
Et une tuile n'a pas toujours la même taille exacte, donc je ne peux pas le faire non plus. Je n'utilise pas de rectangles.
Deukalion
Autrement dit: je veux seulement parcourir les objets qui DEVRAIENT être rendus, pas parcourir les objets qui ne devraient pas du tout.
Deukalion
Le code de @Deukalion Riktothepast ne fait que parcourir les tuiles qui devraient apparaître dans la zone de délimitation de l'écran (bien que ce ne soit pas très clair). La même technique de base peut être utilisée pour parcourir un rectangle de tuiles dans un ensemble de coordonnées donné.
DampeS8N
0

Vous pouvez avoir un Bitmap qui est la scène entière mais non affiché. Et puis un bitmap de couche d'appareil photo de la taille d'un écran qui ne fait que dessiner à partir de la scène entière mais seulement de la partie qui doit être affichée.

mrall
la source