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;
}
xna
c#
collision-detection
platformer
collision-resolution
PriestVallon
la source
la source
Réponses:
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.
Voici la version nettoyée du code: http://pastie.org/3152377
Et voici une vidéo de cet échantillon en action: http://www.youtube.com/watch?v=5-D0PGdoDDY
Et si je me souviens bien, la vraie raison en était quelque chose comme ceci:
la source
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.
la source