矩形对角线移动后的碰撞处理问题求助
平台游戏对角线移动碰撞解决问题
我正在制作一款简单的平台游戏,但遇到了对角线移动后的碰撞解决问题。我使用瓦片地图(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
相关产品推荐
相关产品推荐

