You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

HTML Canvas/Javascript平台游戏碰撞检测问题求助

修复Canvas平台游戏横向碰撞检测问题

你猜得完全没错——底部碰撞的位置修正确实会干扰横向检测!问题出在:当你在处理底部碰撞时直接修改了player1.pos[1],但后续横向检测用的player_leftxplayer_rightx这些坐标还是基于修改前的Y位置计算的,这就导致横向检测的tile坐标已经不准确了,自然会出现逻辑错误。

不需要重构整个函数,只需要调整检测和位置修正的顺序,让垂直和水平方向的检测互不干扰,具体可以按下面的思路修改:

核心修复思路

  1. 一次性计算所有检测所需的Tile坐标:基于玩家当前的初始位置,先算出四个角对应的所有Tile和它们的solid属性,确保所有检测逻辑用的都是同一套初始位置数据。
  2. 分离碰撞检测与位置修正:先分别计算垂直、水平方向需要调整的偏移量,最后统一应用到玩家位置上,而不是改一次位置就影响后续检测。

修改后的代码示例

function tileCheck(world){
    // 第一步:基于当前玩家的初始位置,一次性计算所有Tile坐标
    const currentPlayerY = player1.pos[1];
    const currentPlayerX = player1.pos[0];
    
    const player_topy = Math.ceil(currentPlayerY / world.squareHeight) || 0;
    const player_bottomy = Math.ceil((currentPlayerY + player1.height)/world.squareHeight) || 0;
    const player_leftx = Math.floor(currentPlayerX / world.squareWidth) || 0;
    const player_rightx = Math.floor((currentPlayerX + player1.width)/world.squareWidth) || 0;

    // 获取四个角对应的方块(保持你原代码的Y轴偏移逻辑)
    const topRightBlock = getSquare(world1, player_rightx, player_topy - 1);
    const topLeftBlock = getSquare(world1, player_leftx, player_topy - 1);
    const bottomRightBlock = getSquare(world1, player_rightx, player_bottomy - 1);
    const bottomLeftBlock = getSquare(world1, player_leftx, player_bottomy - 1);

    // 获取方块的solid属性
    const topRightSol = world.legend[topRightBlock].solid;
    const topLeftSol = world.legend[topLeftBlock].solid;
    const bottomRightSol = world.legend[bottomRightBlock].solid;
    const bottomLeftSol = world.legend[bottomLeftBlock].solid;

    // 初始化偏移量和状态
    let yOffset = 0;
    let xOffset = 0;
    player1.grounded = false;
    player1.wallLeft = false;
    player1.wallRight = false;
    player1.vel[1] = 0; // 默认重置垂直速度,有碰撞时会保留设置
    player1.vel[0] = 0; // 默认重置水平速度,有碰撞时会保留设置

    // 处理垂直方向碰撞(底部优先于顶部,避免同时碰撞的矛盾)
    if((bottomRightSol && !topRightSol) || (bottomLeftSol && !topLeftSol)){
        // 计算需要向上偏移的量,让玩家刚好落在方块顶部
        yOffset = (player_bottomy - 1)*world.squareHeight - (currentPlayerY + player1.height);
        player1.grounded = true;
    } else if((topLeftSol && !bottomLeftSol) || (topRightSol && !bottomRightSol)){
        // 计算需要向下偏移的量,让玩家刚好碰到方块底部
        yOffset = player_topy*world.squareHeight - currentPlayerY;
    }

    // 处理水平方向碰撞
    if(topLeftSol && bottomLeftSol){
        // 计算向右偏移的量,让玩家刚好离开左侧方块
        xOffset = (player_leftx + 1)*world.squareWidth - currentPlayerX;
        player1.wallLeft = true;
    }
    if(topRightSol && bottomRightSol){
        // 计算向左偏移的量,让玩家刚好离开右侧方块
        xOffset = (player_rightx)*world.squareWidth - (currentPlayerX + player1.width);
        player1.wallRight = true;
    }

    // 最后统一应用所有偏移量,确保垂直和水平检测互不干扰
    player1.pos[1] += yOffset;
    player1.pos[0] += xOffset;
}

关键修改点说明

  • 所有Tile坐标都基于玩家的初始位置计算,不会因为中途修改位置而失效。
  • 垂直和水平方向的偏移量分开计算,最后一次性应用,彻底避免了底部修正影响横向检测的问题。
  • 状态变量(groundedwallLeft等)提前初始化,减少逻辑分支里的状态遗漏风险。

额外优化建议

如果你想进一步提升碰撞检测的准确性,可以考虑:

  • 改用AABB轴对齐碰撞检测:检测玩家的整个 bounding box 与方块的重叠区域,而不是仅依赖四个角,能处理玩家部分进入方块的边缘情况。
  • 添加碰撞优先级逻辑:比如当玩家同时撞到地面和墙面时,优先处理地面碰撞,避免出现奇怪的位置跳跃。

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

火山引擎 最新活动