Das von Microsoft bereitgestellte Beispiel scheint, als ob die Kollisionserkennung (soweit ich sehen kann) einen kleinen Fehler aufweist. Wenn der Benutzer mit einer nicht passierbaren Kachel kollidiert, wird die Tiefe der Kreuzung berechnet. Der kleinere der Tiefenwerte X und Y wird verwendet, um die Position des Benutzers so festzulegen, dass er nicht mehr mit der Kachel kollidiert. Aber wenn der Benutzer diagonal unterwegs wäre, könnte dies dazu führen, dass der Benutzer nicht genau an dem Punkt landet, an dem der Charakter zuerst mit der Kachel kollidieren würde?
Ich liege wahrscheinlich falsch, aber so sehe ich das auch.
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
quelle
quelle
Antworten:
Du hast absolut recht . Ich hatte einige Probleme mit den Kollisionsroutinen im XNA-Platformer-Beispiel. Aber ich habe es geschafft, mit dem im Beispiel angegebenen Code zu beginnen und ihn ein wenig zu ändern, bis ich in jedem Testszenario, das ich darauf werfen konnte, konsistente Ergebnisse erzielt habe.
Das Problem, das ich hatte, war insbesondere, als ich versuchte, an einer Wand entlang zu gleiten, indem ich mich diagonal dagegen bewegte. Aufgrund der Annahme, die die Probe macht, um Kollisionen basierend auf der kleinsten Verschiebungsachse aufzulösen, führte dies dazu, dass sich der Charakter nicht bewegen konnte, wenn er gegen eine Wand in eine Richtung drückte . Wenn ich zum Beispiel ein Schild benutze, stecke ich fest, wenn ich mich an die Decke drücke und versuche, mich von links nach rechts dagegen zu bewegen (ich kann mich nicht an die Einzelheiten erinnern). Ein Wechsel des Vorzeichens würde diese Situation lösen, aber im umgekehrten Szenario würde ein Problem auftreten. Fazit ist, dass ich mit der bereitgestellten Implementierung nicht in allen Seiten und aus allen Richtungen richtig funktionieren konnte - es würde immer in mindestens einem Fall fehlschlagen.
Der Kern der Änderungen, die ich vorgenommen habe, bestand darin, die Bewegung auf der X-Achse unabhängig von der Bewegung auf der Y-Achse in zwei getrennten Schritten zu handhaben. Ich habe bereits in dieser Antwort darüber geschrieben, gehen Sie also zu den Details.
Hier ist die bereinigte Version des Codes: http://pastie.org/3152377
Und hier ist ein Video dieses Beispiels in Aktion: http://www.youtube.com/watch?v=5-D0PGdoDDY
Und wenn ich mich richtig erinnere, war der eigentliche Grund dafür ungefähr so:
quelle
Wenn Sie mehrere Kollisionen haben, wenn Sie Ihre Kollisionen von der nächstgelegenen zur entferntesten Entfernung von der Mitte jedes betroffenen Rechtecks korrigieren, tritt das Problem des "Hängens" nicht auf.
1) Finden Sie alle kollidierenden Rechtecke
2) Wenn es mehr als eine gibt (abhängig von Ihrem Anwendungsfall kann dies häufig oder selten sein), suchen Sie die nächstgelegene.
3) Lösen Sie Kollisionen nacheinander und prüfen Sie, ob die anderen Kollisionen noch gültig sind
In der akzeptierten Antwort sind die Kollisions- und Eingabelogik schlammig; Es werden Überprüfungen durchgeführt, um den Kurs usw. zu bestimmen. Durch die Implementierung in der beschriebenen Weise wird die Kollisionslogik von der Eingangslogik getrennt, und zwar auf Kosten der Berechnung der Entfernung, falls erforderlich.
quelle