Exemple de plateforme Microsoft XNA, la détection de collision est-elle implémentée avec précision?

11

L'exemple fourni par Microsoft semble que la détection de collision (d'après ce que je peux voir) aura une petite erreur. Lorsque l'utilisateur entre en collision avec une tuile Unpassable, la profondeur de l'intersection est calculée. La plus petite des valeurs de profondeur X et Y est utilisée pour fixer la position de l'utilisateur afin qu'elle n'entre plus en collision avec la tuile. Mais si l'utilisateur voyageait en diagonale, cela pourrait-il empêcher l'utilisateur de se retrouver précisément au point où le personnage entrerait d'abord en collision avec la tuile?

Je me trompe probablement, mais c'est juste ainsi que je le vois.

   private void HandleCollisions()
        {
            // Get the player's bounding rectangle and find neighboring tiles.
            Rectangle bounds = BoundingRectangle;
            int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
            int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
            int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
            int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

            // Reset flag to search for ground collision.
            isOnGround = false;

            // For each potentially colliding tile,
            for (int y = topTile; y <= bottomTile; ++y)
            {
                for (int x = leftTile; x <= rightTile; ++x)
                {
                    // If this tile is collidable,
                    TileCollision collision = Level.GetCollision(x, y);
                    if (collision != TileCollision.Passable)
                    {
                        // Determine collision depth (with direction) and magnitude.
                        Rectangle tileBounds = Level.GetBounds(x, y);
                        Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                        if (depth != Vector2.Zero)
                        {
                            float absDepthX = Math.Abs(depth.X);
                            float absDepthY = Math.Abs(depth.Y);

                            // Resolve the collision along the shallow axis.
                            if (absDepthY < absDepthX || collision == TileCollision.Platform)
                            {
                                // If we crossed the top of a tile, we are on the ground.
                                if (previousBottom <= tileBounds.Top)
                                    isOnGround = true;

                                // Ignore platforms, unless we are on the ground.
                                if (collision == TileCollision.Impassable || IsOnGround)
                                {
                                    // Resolve the collision along the Y axis.
                                    Position = new Vector2(Position.X, Position.Y + depth.Y);

                                    // Perform further collisions with the new bounds.
                                    bounds = BoundingRectangle;
                                }
                            }
                            else if (collision == TileCollision.Impassable) // Ignore platforms.
                            {
                                // Resolve the collision along the X axis.
                                Position = new Vector2(Position.X + depth.X, Position.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                    }
                }
            }

            // Save the new bounds bottom.
            previousBottom = bounds.Bottom;
        }
PriestVallon
la source
3
Pourquoi le moins 1, ppl? La question est valable pour moi. Mais voici une réponse courte: la démo de plate-forme fournie avec XNA n'est qu'un exemple, de toute façon. À ne pas suivre strictement comme modèle pour vos jeux. C'est pour vous montrer qu'un jeu PEUT être fait. Vous ne devriez pas vous embêter si sa mise en œuvre n'est pas la meilleure du tout.
Gustavo Maciel
Merci, j'ai juste supposé avec l'exemple que ce qu'ils avaient fait était la meilleure façon de le faire et qu'il me manquait quelque chose. Merci de l'avoir éclairci pour moi.
PriestVallon

Réponses:

12

Vous avez absolument raison . J'ai eu ma part de problèmes avec les routines de collision sur l'exemple de plateforme XNA. Mais j'ai réussi à partir du code fourni dans l'exemple et à le modifier un peu jusqu'à ce que j'obtienne des résultats cohérents dans chaque scénario de test que je pouvais lui lancer.

En particulier, le genre de problème que j'avais était d'essayer de glisser le long d'un mur en se déplaçant en diagonale contre lui. En raison de l'hypothèse que l'échantillon fait pour résoudre les collisions sur la base du plus petit axe de déplacement, cela a empêché le personnage de bouger lorsqu'il a poussé contre un mur dans une certaine direction. Par exemple, en utilisant un seul signe, je me retrouvais coincé lorsque j'embrassais le plafond et essayais de me déplacer contre lui de gauche à droite (je ne me souviens pas des détails). Changer le signe résoudrait cette situation mais un problème apparaîtrait dans le scénario opposé. En fin de compte, avec l'implémentation fournie, je ne pouvais pas le faire fonctionner correctement dans tous les côtés et dans toutes les directions - cela échouerait toujours dans au moins un cas.

Donc, l'essentiel des changements que j'ai faits consistait à commencer à gérer le mouvement sur l'axe X indépendamment du mouvement sur l'axe Y, sur deux étapes distinctes. J'ai déjà écrit à ce sujet dans cette réponse, alors allez-y pour les détails.

Et si je me souviens bien, la vraie raison en était quelque chose comme ceci:

entrez la description de l'image ici

David Gouveia
la source
1
David toujours sur XNA!
Gustavo Maciel
1
@ Gustavo-Gtoknu J'ai senti qu'il fallait encore un dessin du problème: P
David Gouveia
1
Je viens de tomber sur cette réponse - excellent travail! Merci David.
Austin Brunkhorst
1

Lorsque vous avez plusieurs collisions, si vous rectifiez vos collisions du plus proche au plus éloigné du centre de chaque rectangle impliqué, vous n'aurez pas le problème de "suspendre".

1) Trouvez tous les rectangles en collision

2) S'il y en a plusieurs (selon votre cas d'utilisation, cela peut être fréquent ou peu fréquent), trouvez le plus proche.

3) Résolvez les collisions une par une et vérifiez si les autres sont toujours des collisions valides

Dans la réponse acceptée, la logique de collision et d'entrée est boueuse; il a des vérifications pour déterminer le cap, etc. L'implémenter de la manière décrite maintient la logique de collision distincte de la logique d'entrée au détriment du calcul de la distance si nécessaire.

dossier accordéon
la source