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); }
逻辑说明
- 先执行X轴移动,若碰撞则回退X位置,否则保留X轴移动结果
- 基于X轴的最终位置执行Y轴移动,若碰撞则回退Y位置,否则保留Y轴移动结果
- 无论对角线移动时哪一轴碰撞,另一轴的有效移动都会被保留,完全替代原有的“整体回退”逻辑
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

