Esempio di Microsoft XNA Platformer, il Collsion Detection è implementato con precisione?


11

L'esempio fornito da Microsoft sembra che il rilevamento delle collisioni (da quello che posso vedere) avrà un piccolo errore. Quando l'utente si scontra con una tessera Insuperabile, viene calcolata la profondità dell'intersezione. Il più piccolo dei valori di profondità X e Y viene utilizzato per fissare la posizione dell'utente in modo che non si scontri più con la piastrella. Ma se l'utente viaggiasse in diagonale, ciò potrebbe comportare che l'utente non finisca esattamente nel punto in cui il personaggio si scontrerebbe per la prima volta con la tessera?

Probabilmente sbaglio, ma è così che lo vedo.

   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;
        }

3
Perché il meno 1, ppl? La domanda è valida per me Ma ecco una risposta breve: la demo del platform fornita con XNA è comunque solo un esempio. Da non seguire rigorosamente come modello per i tuoi giochi. Ti mostra che un gioco PU game essere fatto. Non dovresti preoccuparti se la sua implementazione non è la migliore.
Gustavo Maciel,

Grazie, ho solo supposto con l'esempio che quello che avevano fatto era il modo migliore di fare e che mi mancava qualcosa. Grazie per avermi chiarito.
Sacerdote Vallon

Risposte:


12

Hai assolutamente ragione . Ho avuto la mia parte di problemi con le routine di collisione sul campione del platform XNA. Ma sono riuscito a partire dal codice come fornito nell'esempio e l'ho modificato un po 'fino a quando non ho ottenuto risultati coerenti in ogni scenario di test che ho potuto lanciarci.

In particolare, il tipo di problema che stavo riscontrando era quando cercavo di scivolare lungo un muro muovendomi diagonalmente contro di esso. A causa dell'assunto che il campione fa per risolvere le collisioni basate sull'asse di spostamento più piccolo, ciò ha portato il personaggio a non potersi muovere spingendo contro un muro in una direzione. Ad esempio, usando un solo segno, resterei bloccato quando abbracciavo il soffitto e provavo a spostarmi da sinistra a destra (non ricordo i dettagli). Cambiare il segno risolverebbe quella situazione ma un problema apparirebbe nello scenario opposto. La linea di fondo è che con l'implementazione fornita non sono riuscito a farlo funzionare correttamente in tutti i lati e da ogni direzione - avrebbe sempre fallito in almeno un caso.

Quindi il nucleo dei cambiamenti che ho fatto è stato quello di iniziare a gestire il movimento sull'asse X indipendentemente dal movimento sull'asse Y, su due passi separati. Ne ho già parlato in questa risposta, quindi andate lì per i dettagli.

E se ricordo bene, il vero motivo era qualcosa del genere:

inserisci qui la descrizione dell'immagine


1
David fa sempre affidamento su XNA!
Gustavo Maciel,

1
@ Gustavo-Gtoknu Ho sentito che aveva ancora bisogno di un disegno del problema: P
David Gouveia,

1
Ho appena trovato questa risposta - ottimo lavoro! Grazie David.
Austin Brunkhorst,

1

Quando si verificano più collisioni se si correggono le collisioni dal più vicino al più lontano dal centro di ciascun rettangolo interessato, non si avrà il problema di "impiccagione".

1) Trova tutti i rettangoli in collisione

2) Se ce ne sono più di uno (a seconda del caso d'uso questo può essere frequente o poco frequente), trova il più vicino.

3) Risolvi gli scontri uno alla volta e controlla se gli altri sono ancora validi

Nella risposta accettata la collisione e la logica di input sono confuse; ha dei controlli per determinare la direzione, ecc. L'implementazione nel modo descritto mantiene la logica di collisione separata dalla logica di input al costo del calcolo della distanza quando necessario.

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.