You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

矩形对角线移动后的碰撞处理问题求助

平台游戏对角线移动碰撞解决问题

我正在制作一款简单的平台游戏,但遇到了对角线移动后的碰撞解决问题。我使用瓦片地图(tile map),可以遍历玩家周围的所有瓦片。

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

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

上述代码在垂直或水平移动时运行完全正常,但问题出现在对角线移动时。例如:当玩家处于目标位置时,按下右键+下键,碰撞解决会将玩家放置到瓦片的上方和左侧,但我希望将玩家放置到瓦片上方,同时保留向右的移动效果。


解决思路与方案

核心问题分析

当前碰撞处理逻辑会同时修正X轴和Y轴的位置,对角线移动时两个轴的碰撞处理会互相干扰。比如按下右下移动时,代码会同时把玩家拉到瓦片左侧(X轴修正)和瓦片上方(Y轴修正),但实际上只需要处理Y轴碰撞,保留X轴的移动结果。

方案1:基于重叠量优先处理碰撞

计算玩家与瓦片在X、Y轴上的重叠距离,优先处理重叠量更小的轴——这个轴是玩家“刚碰到”的方向,修正后另一个轴的移动可能就不会产生碰撞了。

修改后的CORE_ENTITY_ResolveTileCollision函数:

static void CORE_ENTITY_ResolveTileCollision(entity_t *e, tile_t *t)
{
    // 计算X轴和Y轴的重叠量(正数表示重叠)
    float overlapX = fmin(e->x + e->w, t->x + t->w) - fmax(e->x, t->x);
    float overlapY = fmin(e->y + e->h, t->y + t->h) - fmax(e->y, t->y);

    // 优先处理重叠量更小的轴,避免同时修正两个方向
    if (fabs(overlapX) < fabs(overlapY)) {
        // 处理X轴碰撞
        if (e->dx < 0) {
            e->x = t->x + t->w;
        } else if (e->dx > 0) {
            e->x = t->x - e->w;
        }
    } else {
        // 处理Y轴碰撞
        if (e->dy < 0) {
            e->y = t->y + t->h;
        } else if (e->dy > 0) {
            e->y = t->y - e->h;
            e->isOnGround = true;
        }
    }
}

方案2:拆分X/Y轴分步处理

把玩家的移动拆成X轴和Y轴两个独立步骤:先移动X轴并处理X轴碰撞,再移动Y轴并处理Y轴碰撞。这种方式能彻底避免两个轴的碰撞处理互相干扰。

示例伪代码逻辑:

// 先处理X轴移动与碰撞
e->x += e->dx;
CORE_ENTITY_CheckAndResolveXAxisCollisions(e); // 只处理X轴相关碰撞

// 再处理Y轴移动与碰撞
e->y += e->dy;
CORE_ENTITY_CheckAndResolveYAxisCollisions(e); // 只处理Y轴相关碰撞

你可以修改CORE_ENTITY_CheckAroundTiles函数,添加参数控制只处理某一轴的碰撞;或者在碰撞检测时,只针对当前轴的重叠进行判断。

额外优化建议

  • 处理Y轴碰撞时,建议先重置isOnGround为false,只有当确实碰到地面时才设为true,避免状态异常。
  • 每次修正位置后,可以重新检测一次碰撞,避免一次移动中多次碰撞导致的位置错误。

内容的提问来源于stack exchange,提问作者Zecksidd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 22:34:59