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

JavaScript碰撞检测问题:英雄移动未贴合碰撞块及速度调节矛盾

问题根源

当前碰撞逻辑的核心问题:当英雄以较大步长(比如10像素)移动时,碰撞检测触发时机是英雄已经尝试移动到步长叠加后的位置,此时直接停止会让英雄停在“穿透”碰撞区域或与碰撞块有间隙的位置。另外,setInterval受浏览器最小间隔限制,无法通过缩小间隔提升步长为1时的移动速度,反而可能引发性能问题。

解决方案

一、保持步长为1时的移动速度与流畅性

放弃setInterval,改用requestAnimationFrame实现基于时间的移动控制。这种方式与浏览器刷新同步,更流畅,且可通过「每秒移动像素数」精准控制速度,不受步长大小限制。

示例代码:

let interV;
let lastTime = 0;
const moveSpeed = 150; // 每秒移动150像素,可按需调整

function runH(xDir, yDir) {
    cancelAnimationFrame(interV);
    inmotion = true;
    
    // 标准化方向向量,确保方向正确且不影响速度计算
    const dirX = xDir !== 0 ? xDir / Math.abs(xDir) : 0;
    const dirY = yDir !== 0 ? yDir / Math.abs(yDir) : 0;

    function moveHero(currentTime) {
        if (!lastTime) lastTime = currentTime;
        const deltaTime = (currentTime - lastTime) / 1000; // 转换为秒级时间差
        lastTime = currentTime;

        const HeroP = $("#BTB_Hero").position();
        const HeroW = $("#BTB_Hero").width();
        const HeroH = $("#BTB_Hero").height();
        
        // 根据时间差计算实际移动距离
        const moveX = dirX * moveSpeed * deltaTime;
        const moveY = dirY * moveSpeed * deltaTime;
        let HeroX = HeroP.left + moveX;
        let HeroY = HeroP.top + moveY;

        let collision = false;
        let collisionBlock = null;

        // 碰撞检测并记录碰撞块信息
        for (let i = 0; i < blocksA.length; i++) {
            const block = $(blocksA[i]);
            const blockP = block.position();
            const blockX = blockP.left;
            const blockY = blockP.top;
            const blockW = block.width();
            const blockH = block.height();

            if (HeroX + HeroW >= blockX && HeroX <= blockX + blockW && HeroY + HeroH >= blockY && HeroY <= blockY + blockH) {
                collision = true;
                collisionBlock = { x: blockX, y: blockY, w: blockW, h: blockH };
                break;
            }
        }

        if (collision) {
            // 碰撞时修正位置到贴合状态
            if (dirX > 0) HeroX = collisionBlock.x - HeroW; // 向右移动,贴住块左侧
            else if (dirX < 0) HeroX = collisionBlock.x + collisionBlock.w; // 向左移动,贴住块右侧
            if (dirY > 0) HeroY = collisionBlock.y - HeroH; // 向下移动,贴住块顶部
            else if (dirY < 0) HeroY = collisionBlock.y + collisionBlock.h; // 向上移动,贴住块底部
            
            $("#BTB_Hero").css({ "top": HeroY + "px", "left": HeroX + "px" });
            inmotion = false;
        } else {
            $("#BTB_Hero").css({ "top": HeroY + "px", "left": HeroX + "px" });
            interV = requestAnimationFrame(moveHero);
        }

        checkLevelChange(HeroX, HeroY);
    }

    interV = requestAnimationFrame(moveHero);
}

二、步长为10时碰撞后贴合碰撞块

如果坚持使用原有setInterval逻辑,需在碰撞检测触发时,根据移动方向计算英雄的贴合位置,更新位置后再停止移动。

修改后的碰撞处理代码(需配合碰撞块记录逻辑):

function runH(x, y) {
    clearInterval(interV);
    inmotion = true;
    interV = setInterval(function () {
        var HeroP = $("#BTB_Hero").position();
        var HeroW = $("#BTB_Hero").width();
        var HeroH = $("#BTB_Hero").height();
        var HeroX = HeroP.left + x;
        var HeroY = HeroP.top + y;
        
        let collision = false;
        let collisionBlock = null;
       
        for (let i = 0; i < blocksA.length; i++){
         var block = $(blocksA[i]);
         var blockP = block.position();
         var blockX = blockP.left;
         var blockY = blockP.top;
         var blockW = block.width();
         var blockH = block.height();
         
         if ( HeroX + HeroW >= blockX && HeroX <= blockX + blockW && HeroY + HeroH >= blockY && HeroY <= blockY + blockH) {
             collision = true;
             collisionBlock = { x: blockX, y: blockY, w: blockW, h: blockH };
             break;
         }
     }
     if (collision) {
        // 根据移动方向修正到贴合位置
        let finalX = HeroP.left;
        let finalY = HeroP.top;

        if (x > 0) finalX = collisionBlock.x - HeroW; // 右移贴块左
        else if (x < 0) finalX = collisionBlock.x + collisionBlock.w; // 左移贴块右
        if (y > 0) finalY = collisionBlock.y - HeroH; // 下移贴块顶
        else if (y < 0) finalY = collisionBlock.y + collisionBlock.h; // 上移贴块底

        $("#BTB_Hero").css({ "top": finalY + "px", "left": finalX + "px" });
        clearInterval(interV);
        inmotion = false;
      } else {
          $("#BTB_Hero").css({ "top": HeroY + "px", "left": HeroX + "px" });
      }
        checkLevelChange(HeroX,HeroY);
    }, 16); // 改用16ms间隔,接近浏览器60帧刷新率
}
关键说明
  • 基于requestAnimationFrame的时间驱动方案是最优选择,同时解决了步长1时的速度问题与碰撞贴合问题,移动端体验更流畅。
  • 碰撞贴合的核心逻辑:不要在碰撞时直接停止,而是根据移动方向将英雄位置修正到与碰撞块边缘完全贴合的坐标。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 05:04:55