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

游戏侧边Collision实现存在抖动问题,请求技术支持(附代码)

解决侧边碰撞检测的抖动问题

嘿,我完全懂这种感受——功能都正常,但那烦人的抖动真的能把游戏体验拉垮!咱们来一步步解决这个问题。

问题根源

你现在的逻辑是先移动玩家,再检测碰撞并修正位置,加上两个独立的setInterval(一个创建物体,一个做碰撞检测),时间上的不同步会让玩家先“穿”进物体,再被拉出来,视觉上就表现为抖动。另外还有个小问题:你创建的所有物体都用了同一个id="powerup",这在DOM里是无效的(ID必须唯一),虽然暂时没崩,但也得修好。

修复方案

我会把所有更新逻辑整合到同一个requestAnimationFrame循环里(它和浏览器刷新同步,比setInterval更稳定),并且先检测碰撞再执行移动,彻底避免“穿进去再拉出来”的情况。

修改后的完整代码

<!DOCTYPE html>
<html>
<head>
    <title>Game</title>
    <style>
        html, body { height: 100%; width: 100%; margin: 0; }
        #player { background: #000; position: fixed; height: 10px; width: 10px; left: 0px; top: 0px; }
        .object { background: blue; position: fixed; height: 10px; width: 10px; }
    </style>
</head>
<body>
    <div id="player"></div>
    <script>
        const player = document.getElementById("player");
        let objects = [];
        let counter = 0;
        let keysPressed = {};

        // 监听键盘按下/松开,记录按键状态
        document.addEventListener('keydown', (e) => keysPressed[e.keyCode] = true);
        document.addEventListener('keyup', (e) => keysPressed[e.keyCode] = false);

        // 创建物体(用setTimeout控制,避免和主循环冲突)
        function createObject() {
            counter++;
            const newObject = document.createElement("DIV");
            newObject.setAttribute("class", "object");
            newObject.style.top = `${Math.floor(Math.random() * (window.innerHeight - 10)) + 10}px`;
            newObject.style.left = `${Math.floor(Math.random() * (window.innerWidth - 10)) + 10}px`;
            document.body.appendChild(newObject);
            objects.push(newObject);
            
            if (counter <= 10) {
                setTimeout(createObject, 1);
            }
        }

        // 碰撞检测:判断目标位置是否会碰撞
        function willCollide(targetLeft, targetTop) {
            const playerWidth = player.offsetWidth;
            const playerHeight = player.offsetHeight;

            for (const obj of objects) {
                const objLeft = obj.offsetLeft;
                const objTop = obj.offsetTop;
                const objWidth = obj.offsetWidth;
                const objHeight = obj.offsetHeight;

                // 判断玩家目标位置是否和物体重叠
                const overlapX = targetLeft < objLeft + objWidth && targetLeft + playerWidth > objLeft;
                const overlapY = targetTop < objTop + objHeight && targetTop + playerHeight > objTop;

                if (overlapX && overlapY) {
                    return true;
                }
            }
            return false;
        }

        // 主更新循环
        function update() {
            const moveStep = 1;
            let newLeft = player.offsetLeft;
            let newTop = player.offsetTop;

            // 根据按键计算目标位置
            if (keysPressed[40]) { // 下
                newTop += moveStep;
            }
            if (keysPressed[39]) { // 右
                newLeft += moveStep;
            }
            if (keysPressed[38]) { // 上
                newTop -= moveStep;
            }
            if (keysPressed[37]) { // 左
                newLeft -= moveStep;
            }

            // 先检测如果移动到目标位置会不会碰撞,如果不碰撞才执行移动
            if (!willCollide(newLeft, newTop)) {
                player.style.left = `${newLeft}px`;
                player.style.top = `${newTop}px`;
            }

            requestAnimationFrame(update);
        }

        // 启动游戏
        createObject();
        requestAnimationFrame(update);
    </script>
</body>
</html>

关键修改点

  • 统一更新循环:用requestAnimationFrame替代两个独立的setInterval,确保所有操作(移动、碰撞检测)都在同一帧内完成,消除时间差。
  • 先检测后移动:计算出玩家的目标位置后,先判断这个位置是否会和物体碰撞,只有安全才移动,彻底避免“穿模后修正”的抖动。
  • 按键状态管理:改用监听按键的按下/松开状态,比原来的onkeydown更流畅(按住方向键时会持续移动,不用依赖键盘重复触发)。
  • 修复重复ID:移除了重复的id="powerup",改用数组存储物体引用,操作更高效。

这样修改后,碰撞会变得平滑,再也不会有抖动啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:01:28