Przykład platformy Microsoft XNA: czy wykrywanie kolizji jest prawidłowo wdrożone?

11

Przykład podany przez Microsoft wydaje się, że wykrywanie kolizji (z tego, co widzę) będzie miało mały błąd. Gdy użytkownik zderzy się z kaflem Nieprzejezdnym, zostanie obliczona głębokość skrzyżowania. Mniejsza z wartości głębokości X i Y służy do ustalenia pozycji użytkownika, aby nie kolidowała już z kafelkiem. Ale jeśli użytkownik podróżował po przekątnej, czy mogłoby to spowodować, że nie skończy on dokładnie w punkcie, w którym postać najpierw zderzy się z płytką?

Prawdopodobnie się mylę, ale właśnie tak to widzę.

   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
źródło
3
Dlaczego minus 1, ppl? Pytanie jest dla mnie ważne. Ale oto krótka odpowiedź: Demo platformówki dostarczane z XNA jest zresztą tylko przykładem. Nie należy się do tego ściśle stosować jako modelu dla swoich gier. Ma pokazać, że można zrobić grę. Nie powinieneś się przejmować, jeśli jego wdrożenie wcale nie jest najlepsze.
Gustavo Maciel
Dzięki, właśnie założyłem z przykładem, że to, co zrobili, było najlepszym sposobem na zrobienie tego i że czegoś mi brakowało. Dzięki za wyjaśnienie mi tego.
PriestVallon

Odpowiedzi:

12

Masz absolutną rację . Miałem swój udział w problemach z procedurami kolizji na próbce platformówki XNA. Ale udało mi się zacząć od kodu podanego w próbce i zmodyfikowałem go trochę, dopóki nie uzyskałem spójnych wyników w każdym scenariuszu testowym, jaki mogłem na niego rzucić.

W szczególności miałem problem z próbą ześlizgnięcia się po ścianie, przesuwając się po niej po przekątnej. Z powodu założenia przyjętego przez próbkę w celu rozstrzygnięcia kolizji w oparciu o najmniejszą oś przemieszczenia, to spowodowało, że postać nie mogła się poruszać, pchając ścianę w jakimś kierunku. Na przykład, używając jednego znaku, utknąłem podczas przytulania sufitu i próby poruszania się po nim od lewej do prawej (nie pamiętam szczegółów). Zmiana znaku rozwiązałaby tę sytuację, ale problem pojawiłby się w przeciwnym scenariuszu. Najważniejsze jest to, że przy zapewnionej implementacji nie mogłem sprawić, aby działał poprawnie ze wszystkich stron i ze wszystkich kierunków - zawsze zawiódłby w co najmniej jednym przypadku.

Tak więc sedno zmian, które wprowadziłem, polegały na rozpoczęciu obsługi ruchu na osi X niezależnie od ruchu na osi Y, w dwóch oddzielnych krokach. Pisałem o tym wcześniej w tej odpowiedzi, więc idź tam po szczegóły.

I jeśli dobrze pamiętam, faktyczny powód był następujący:

wprowadź opis zdjęcia tutaj

David Gouveia
źródło
1
David zawsze gra na XNA!
Gustavo Maciel
1
@ Gustavo-Gtoknu Czułem, że nadal potrzebuję narysować problem: P
David Gouveia
1
Właśnie natrafiłem na tę odpowiedź - świetna robota! Dzięki David.
Austin Brunkhorst
1

W przypadku wielu kolizji, jeśli usuniesz kolizje od najbliższego do najdalszego od środka każdego zaangażowanego prostokąta, nie będziesz mieć problemu z „zawieszeniem się”.

1) Znajdź wszystkie kolidujące prostokąty

2) Jeśli jest ich więcej niż jeden (w zależności od przypadku użycia może to być częste lub rzadkie), znajdź najbliższy.

3) Rozwiąż kolizje pojedynczo i sprawdź, czy pozostałe nadal są kolizjami

W przyjętej odpowiedzi logika kolizji i wprowadzania jest mętna; ma kontrole w celu ustalenia kursu itp. Wdrożenie go w opisany sposób oddziela logikę kolizji od logiki wejściowej kosztem obliczania odległości w razie potrzeby.

folder akordeonu
źródło