游戏侧边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
相关产品推荐
相关产品推荐

