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;
}
xna
c#
collision-detection
platformer
collision-resolution
PriestVallon
źródło
źródło
Odpowiedzi:
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.
Oto oczyszczona wersja kodu: http://pastie.org/3152377
A oto film z tej próbki w akcji: http://www.youtube.com/watch?v=5-D0PGdoDDY
I jeśli dobrze pamiętam, faktyczny powód był następujący:
źródło
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.
źródło