矩形对角移动后如何处理碰撞

问题描述 投票:0回答:1

我正在尝试制作一个简单的平台游戏,但在对角线移动后解决碰撞时遇到问题。我有一个图块地图,我可以用它迭代播放器周围的每个图块。

这是检测和处理碰撞的代码:

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;
    }
}

所有这些对于垂直或水平移动都非常有效,但问题在于对角线移动。

例如:

当玩家(蓝色)站在这里时:

如果我按右+下,碰撞解决将替换图块顶部和左侧的玩家,因为它将检测到来自右侧的移动。但我想更换顶部的球员并应用正确的动作。有人可以给我线索吗?

c sdl collision rectangles aabb
1个回答
0
投票

不久前我尝试制作平台游戏时也遇到了类似的问题。

发生这种情况的原因是 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 很陌生,只是想帮助别人。希望这会有所帮助。 :)

© www.soinside.com 2019 - 2024. All rights reserved.