El ejemplo proporcionado por Microsoft parece que la detección de colisión (por lo que puedo ver) tendrá un pequeño error. Cuando el usuario colisiona con un mosaico intransitable, se calcula la profundidad de la intersección. El menor de los valores de profundidad X e Y se usa para fijar la posición del usuario para que ya no colisione con el mosaico. Pero si el usuario viajaba en diagonal, ¿podría esto resultar en que el usuario no terminara precisamente en el punto donde el personaje colisionaría primero con el mosaico?
Probablemente me equivoque, pero así es como lo veo.
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
SacerdoteVallon
fuente
fuente
Respuestas:
Tienes toda la razón . He tenido muchos problemas con las rutinas de colisión en la muestra de plataformas XNA. Pero me las arreglé para comenzar desde el código tal como se proporciona en la muestra, y lo modifiqué un poco hasta que logré resultados consistentes en cada escenario de prueba que pude lanzar.
En particular, el tipo de problema que tenía era cuando trataba de deslizarme a lo largo de una pared moviéndome diagonalmente contra ella. Debido a la suposición que hace la muestra para resolver las colisiones basadas en el eje de desplazamiento más pequeño, esto provocó que el personaje no pudiera moverse al empujar contra una pared en alguna dirección. Por ejemplo, usando un letrero, me quedaba atascado al abrazar el techo e intentar moverme contra él de izquierda a derecha (no recuerdo los detalles). Cambiar el signo resolvería esa situación, pero aparecería un problema en el escenario opuesto. La conclusión es que con la implementación provista no pude hacer que funcione correctamente en todos los lados y desde todas las direcciones ; siempre fallaría en al menos un caso.
Entonces, el núcleo de los cambios que hice fue comenzar a manejar el movimiento en el eje X independientemente del movimiento en el eje Y, en dos pasos separados. He escrito sobre esto antes en esta respuesta, así que dirígete allí para obtener más detalles.
Aquí está la versión limpia del código: http://pastie.org/3152377
Y aquí hay un video de esa muestra en acción: http://www.youtube.com/watch?v=5-D0PGdoDDY
Y si no recuerdo mal, la razón real fue algo como esto:
fuente
Cuando tiene colisiones múltiples si rectifica sus colisiones del más cercano al más alejado del centro de cada rectángulo involucrado, no tendrá el problema de "colgar".
1) Encuentra todos los rectángulos en colisión
2) Si hay más de uno (dependiendo de su caso de uso, esto puede ser frecuente o poco frecuente), busque el más cercano.
3) Resuelva las colisiones de una en una y verifique si las otras siguen siendo válidas.
En la respuesta aceptada, la colisión y la lógica de entrada son confusas; tiene comprobaciones para determinar el rumbo, etc. Implementarlo de la manera descrita mantiene la lógica de colisión separada de la lógica de entrada a costa de calcular la distancia cuando sea necesario.
fuente