이 질문에 대답하기 전에 몇 가지 질문이 있습니다. 첫째, 벽에 붙어있는 원래 버그에서 왼쪽 개별 타일의 타일이 하나의 큰 타일과 반대입니까? 그리고 만약 그렇다면 플레이어가 그들 사이에 갇히게 되었습니까? 두 가지 질문에 모두 맞다면, 새로운 직책이 유효한지 확인하십시오 . 즉, 플레이어에게 이동을 지시하는 위치에 충돌이 있는지 확인해야합니다. 아래에 설명 된대로 따라서 최소 변위를 해결하고 그 기반으로 플레이어를 이동에만 그 경우 수저기로 움직여. 코 아래에서도 거의 : P 이것은 실제로 "코너 케이스"라고하는 또 다른 버그를 소개합니다. 본질적으로 모서리 측면에서 (.gif에서 수평 스파이크가 나오는 왼쪽 하단과 같이 스파이크가없는 경우) 충돌을 해결하지 못합니다. 왜냐하면 생성 한 해상도 중 어느 것도 유효한 위치로 이어지지 않는다고 생각하기 때문입니다. . 이 문제를 해결하려면 충돌이 해결되었는지 여부와 모든 최소 침투 해상도 목록을 유지하십시오. 그런 다음 충돌이 해결되지 않은 경우 생성 된 모든 해상도를 반복하고 최대 X 및 최대 Y 해상도를 추적하십시오 (최대 값이 동일한 해상도에서 나올 필요는 없음). 그런 다음 해당 최대 값의 충돌을 해결하십시오. 이것은 내가 만난 문제뿐만 아니라 모든 문제를 해결하는 것으로 보입니다.
List<Vector2> collisions = new List<Vector2>();
bool resolved = false;
foreach (Platform p in testPlat)
{
Vector2 dif = p.resolveCollision(player.getCollisionMask());
RectangleF newPos = player.getCollisionMask();
newPos.X -= dif.X;
newPos.Y -= dif.Y;
if (!PlatformCollision(newPos)) //This checks if there's a collision (ie if we're entering an invalid space)
{
if (dif.X != 0)
player.velocity.X = 0; //Do whatever you want here, I like to stop my player on collisions
if (dif.Y != 0)
player.velocity.Y = 0;
player.MoveY(-dif.Y);
player.MoveX(-dif.X);
resolved = true;
}
collisions.Add(dif);
}
if (!resolved)
{
Vector2 max = Vector2.Zero;
foreach (Vector2 v in collisions)
{
if (Math.Abs(v.X) > Math.Abs(max.X))
{
max.X = v.X;
}
if (Math.Abs(v.Y) > Math.Abs(max.Y))
{
max.Y = v.Y;
}
}
player.MoveY(-max.Y);
if (max.Y != 0)
player.velocity.Y = 0;
player.MoveX(-max.X);
if (max.X != 0)
player.velocity.X = 0;
}
또 다른 질문은 하나의 타일 또는 개별 타일을 표시하는 스파이크입니까? 그것들이 개별적인 얇은 타일이라면, 수평 타일과 수직 타일에 대해 아래에서 설명하는 것과 다른 접근법을 사용해야 할 수도 있습니다. 그러나 그들이 전체 타일이라면 이것이 작동해야합니다.
기본적으로 이것이 @Trevor Powell이 묘사 한 것입니다. AABB 만 사용하기 때문에 하나의 사각형이 다른 사각형을 얼마나 많이 관통하는지 확인하면됩니다. 그러면 X 축과 Y에 수량이 표시됩니다. 둘 중 최소값을 선택하고 충돌하는 객체를 해당 축을 따라 해당 양만큼 이동합니다. 이것이 AABB 충돌을 해결하는 데 필요한 전부입니다. 이러한 충돌에서 둘 이상의 축을 따라 이동할 필요가 없으므로 최소 이동 만하므로 먼저 이동할 축에 대해 혼동해서는 안됩니다.
메타넷 소프트웨어에는 접근 방식에 대한 고전적인 자습서가 있습니다 . 그것은 또한 다른 형태로 들어갑니다.
다음은 두 사각형의 오버랩 벡터를 찾기 위해 만든 XNA 함수입니다.
public Point resolveCollision(Rectangle otherRect)
{
if (!isCollision(otherRect))
{
return Point.Zero;
}
int minOtherX = otherRect.X;
int maxOtherX = otherRect.X + otherRect.Width;
int minMyX = collisionMask.X;
int maxMyX = collisionMask.X + collisionMask.Width;
int minOtherY = otherRect.Y;
int maxOtherY = otherRect.Y + otherRect.Height;
int minMyY = collisionMask.Y;
int maxMyY = collisionMask.Y + collisionMask.Height;
int dx, dy;
if (maxOtherX - minMyX < maxMyX - minOtherX)
{
dx = (maxOtherX - minMyX);
}
else
{
dx = -(maxMyX - minOtherX);
}
if (maxOtherY - minMyY < maxMyY - minOtherY)
{
dy = (maxOtherY - minMyY);
}
else
{
dy = -(maxMyY - minOtherY);
}
if (Math.Abs(dx) < Math.Abs(dy))
return new Point(dx, 0);
else
return new Point(0, dy);
}
(나는 그것을 구현하는 더 좋은 방법이 있다고 확신하기 때문에 따르기가 간단하기를 바랍니다 ...)
isCollision (Rectangle)은 말 그대로 XNA의 Rectangle.Intersects (Rectangle)에 대한 호출입니다.
나는 이것을 움직이는 플랫폼으로 테스트했으며 정상적으로 작동하는 것 같습니다. .gif와 유사한 테스트를 더 수행하여 작동하지 않는지 확인하고 다시 알려 드리겠습니다.