Пример, предоставленный Microsoft, выглядит так, как будто обнаружение столкновения (из того, что я вижу) будет иметь небольшую ошибку. Когда пользователь сталкивается с непроходимой плиткой, вычисляется глубина пересечения. Меньшее из значений глубины X и Y используется, чтобы зафиксировать положение пользователя, чтобы оно больше не сталкивалось с плиткой. Но если бы пользователь путешествовал по диагонали, может ли это привести к тому, что пользователь не окажется точно в той точке, где персонаж впервые столкнется с плиткой?
Я, наверное, ошибаюсь, но это именно так, как я это вижу.
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
источник
источник
Ответы:
Вы абсолютно правы . У меня была доля проблем с процедурами коллизий на примере платформера XNA. Но мне удалось начать с кода, представленного в образце, и немного его модифицировать, пока я не достиг согласованных результатов в каждом тестовом сценарии, который я мог бы использовать.
В частности, у меня была такая проблема, когда я пытался скользить вдоль стены, двигаясь по диагонали к ней. Из-за допущения, которое образец делает для разрешения столкновений на основе наименьшей оси смещения, это приводило к тому, что персонаж не мог двигаться при толкании к стене в каком-либо направлении. Например, используя один знак, я застреваю, когда обнимаю потолок и пытаюсь двигаться против него слева направо (не могу вспомнить особенности). Переключение знака решило бы эту ситуацию, но проблема возникла бы в противоположном сценарии. Суть в том, что с предоставленной реализацией я не смог заставить ее работать корректно во всех сторонах и со всех сторон - это всегда было бы неудачным хотя бы в одном случае.
Таким образом, суть изменений, которые я сделал, заключалась в том, чтобы начать обрабатывать движение по оси X независимо от движения по оси Y на двух отдельных шагах. Я уже писал об этом раньше в этом ответе, так что отправляйтесь туда за подробностями.
Вот очищенная версия кода: http://pastie.org/3152377
А вот видео этого образца в действии: http://www.youtube.com/watch?v=5-D0PGdoDDY
И если я правильно помню, действительная причина этого была примерно такая:
источник
Если у вас есть несколько столкновений, если вы исправляете свои столкновения от ближайшего к дальнему от центра каждого задействованного прямоугольника, у вас не будет проблемы «зависания».
1) Найти все сталкивающиеся прямоугольники
2) Если их несколько (в зависимости от вашего варианта использования это может быть часто или редко), найдите ближайший.
3) Разрешайте коллизии по одному и проверяйте, все ли остальные по-прежнему допустимы.
В принятом ответе логика столкновения и ввода нечеткая; он имеет проверки для определения заголовка и т. д. Реализация описанного способа позволяет отделить логику коллизий от логики ввода за счет вычисления расстояния, когда это необходимо.
источник