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

2D游戏对角线碰撞函数实现难题:碰撞后保留单轴移动

2D游戏对角线碰撞优化:保留无碰撞轴移动

核心思路

放弃“移动后回退整体位置”的逻辑,改为分轴验证移动:先单独执行X轴移动并检测碰撞,碰撞则回退X轴;再基于X轴最终位置执行Y轴移动并检测碰撞,碰撞则回退Y轴。这样就能保证对角线移动时,无碰撞的轴会保留移动效果,完全符合需求。


第一步:重构输入处理(分离位移计算与执行)

原代码直接调用player->move()执行移动,导致碰撞时只能整体回退。修改为先计算目标位移,暂存到玩家对象中,留到碰撞阶段分轴处理:

void Game::updateInput()
{
    float dirX = 0;
    float dirY = 0;

    if (GetKeyState('W') < 0) dirY = -1.f;
    if (GetKeyState('A') < 0) dirX = -1.f;
    if (GetKeyState('S') < 0) dirY =  1.f;  
    if (GetKeyState('D') < 0) dirX =  1.f;

    // 计算目标位移(保留对角线速度修正)
    float deltaX = dirX;
    float deltaY = dirY;
    if (dirX != 0.f && dirY != 0.f)
    {
        deltaX *= 0.7f;
        deltaY *= 0.7f;
    }

    // 暂存目标位移,不立即执行移动
    player->setTargetDelta(deltaX, deltaY);
}

需要给Player类添加两个成员变量和对应方法:

// Player类内部
private:
    float targetDeltaX = 0;
    float targetDeltaY = 0;

public:
    void setTargetDelta(float dx, float dy) {
        targetDeltaX = dx;
        targetDeltaY = dy;
    }

    float getTargetDeltaX() { return targetDeltaX; }
    float getTargetDeltaY() { return targetDeltaY; }

第二步:重写碰撞检测逻辑(分轴验证)

将原有的“碰撞后回退”改为分轴执行移动+碰撞验证,确保无碰撞轴的移动被保留:

先封装碰撞检测函数(避免重复代码)

bool checkCollision(Player* player, const std::vector<Tile*>& mapTile, float tileWidth, float tileHeight) {
    Vectorf playerPos = player->getPos();
    float playerWidth = player->getWidth();
    float playerHeight = player->getHeight();

    for (auto& tile : mapTile) {
        if (playerPos.x < tile->coordinate_X + tileWidth &&
            playerPos.x + playerWidth > tile->coordinate_X &&
            playerPos.y < tile->coordinate_Y + tileHeight &&
            playerPos.y + playerHeight > tile->coordinate_Y) {
            return true;
        }
    }
    return false;
}

分轴处理移动与碰撞

// 获取玩家初始位置和目标位移
Vectorf playerPos = player->getPos();
float deltaX = player->getTargetDeltaX();
float deltaY = player->getTargetDeltaY();

// 处理X轴移动
player->setPosition(playerPos.x + deltaX, playerPos.y);
if (checkCollision(player, mapTile, tileWidth, tileHeight)) {
    // X轴碰撞,回退到初始X位置
    player->setPosition(playerPos.x, playerPos.y);
}

// 处理Y轴移动(基于X轴移动后的位置)
Vectorf postXPos = player->getPos();
player->setPosition(postXPos.x, postXPos.y + deltaY);
if (checkCollision(player, mapTile, tileWidth, tileHeight)) {
    // Y轴碰撞,回退到X轴移动后的Y位置
    player->setPosition(postXPos.x, postXPos.y);
}

逻辑说明

  1. 先执行X轴移动,若碰撞则回退X位置,否则保留X轴移动结果
  2. 基于X轴的最终位置执行Y轴移动,若碰撞则回退Y位置,否则保留Y轴移动结果
  3. 无论对角线移动时哪一轴碰撞,另一轴的有效移动都会被保留,完全替代原有的“整体回退”逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:13:22