我正在尝试制作一个简单的平台游戏,但在对角线移动后解决碰撞时遇到问题。我有一个图块地图,我可以用它迭代播放器周围的每个图块。
这是检测和处理碰撞的代码:
static void CORE_ENTITY_CheckAroundTiles(entity_t *e)
{
int tilex = (int)floor((e->x+(e->w/2))/TILE_SIZE);
int tiley = (int)floor((e->y+(e->h)/2)/TILE_SIZE);
tile_t *tile;
for (int dx=tilex-1 ; dx<=tilex+1 ; dx++)
{
for (int dy=tiley-1 ; dy<=tiley+1 ; dy++)
{
// Test collision with map
tile = MAP_GetTile(core->map, dx, dy);
if (tile == NULL) // Tile out of range
continue;
switch (tile->type)
{
case TILETYPE_WALL:
if (CORE_ENTITY_IsCollideTile(e, tile))
CORE_ENTITY_ResolveTileCollision(e, tile);
// MAP_ShowTile(core->map, dx, dy);
break;
case TILETYPE_EMPTY:
break;
}
}
}
}
CORE_ENTITY_IsCollideTile() 函数:
static bool CORE_ENTITY_IsCollideTile(entity_t *e, tile_t *t)
{
if (e->x >= t->x + t->w)
return false;
if (e->x + e->w <= t->x)
return false;
if (e->y >= t->y + t->h)
return false;
if (e->y + e->h <= t->y)
return false;
return true;
}
CORE_ENTITY_ResolveTileCollision() 函数:
static void CORE_ENTITY_ResolveTileCollision(entity_t *e, tile_t *t)
{
if (e->dx < 0) {
// comes from left
e->x = t->x + t->w;
} else if (e->dx > 0) {
// comes from right
e->x = t->x - e->w;
}
if (e->dy < 0) {
// Comes from bottom
e->y = t->y + t->h;
} else if (e->dy > 0) {
// Comes from top
e->y = t->y - e->h;
e->isOnGround = true;
}
}
所有这些对于垂直或水平移动都非常有效,但问题在于对角线移动。
例如:
当玩家(蓝色)站在这里时:
如果我按右+下,碰撞解决将替换图块顶部和左侧的玩家,因为它将检测到来自右侧的移动。但我想更换顶部的球员并应用正确的动作。有人可以给我线索吗?
不久前我尝试制作平台游戏时也遇到了类似的问题。
发生这种情况的原因是 ResolveTileCollision 函数。在此函数中,您检查玩家来自的方向,并根据该方向重置他们的位置。 这意味着 e.dx 为除 0 之外的任意值的碰撞将在 x 方向发生碰撞,并重置到对象的左侧(这与 e.dy 相同)。因此,当实体的运动是对角线时(dx和dy都!= 0),X和Y方向都被重置。
我解决这个错误的方法是使用不同类型的碰撞检测。我会阅读这篇维基百科文章关于碰撞类型的“后验(离散)与先验(连续)”部分。
本质上,后验碰撞发生在碰撞发生之后(如“a posteriori”),然后重置对象位置。 先验碰撞发生在此之前,检查对象是否会发生碰撞,然后停止它们。 通过使用先验碰撞,您永远不必调整玩家坐标,因为它们没有与物体碰撞。
要在代码中实现此功能,请检查实体是否在 X 方向发生碰撞,然后将实体 dx 更改为 0。通过这样做,实体在更新其位置时将不会在 X 方向发生碰撞。对 Y 执行同样的操作。
伪代码(ish):
if (collides(entity.x + dx, entity.y, entity.w, entity.h))
{
entity.dx = 0;
}
if (collides(entity.x, entity.y + dy, entity.w, entity.h))
{
entity.dy = 0;
}
if (collides(entity.x + dx, entity.y + dy, entity.w, entity.h))
{
// also check diagonal collision, AFTER checking horisontal and vertical. Do this so that you cannot clip into things moving diagonally, where you would not on just the x or y. (if youre wondering what that would be like, run it without this if statement, and sometimes youll clip into stuff diagonally. Its a cool and intuitive bug fix :) )
entity.dx = 0;
entity.dy = 0;
}
有时可以按照自己的意愿实施,但请确保单独检查 DX 和 DY,然后再一起检查。
无论如何,我对 stackoverflow 很陌生,只是想帮助别人。希望这会有所帮助。 :)