通过基于速度的2D运动修复简单的碰撞检测

时间:2019-02-21 19:57:38

标签: javascript three.js collision-detection

在此codePen demo中,您可以使用箭头移动“玩家”正方形,放置带有空格的灯光,并且应该通过将其推向相反的方向来阻止从任何方向越过蓝线。 “玩家”使用x和y速度变量来创建运动,如果检测到碰撞,则将它们乘以-1(+某个值)。
问题在于,“玩家”被推离墙壁后被卡在只能从墙壁向后移动的位置,而似乎卡在与墙壁垂直的轴上。 (例如,如果墙位于玩家的顶部,则在碰到墙后只能移至底部,而不能向左或向右移动)
从理论上讲,我想要一个平滑的滑动碰撞检测功能,其中卡在墙上的玩家会缓慢地向左或向右滑动  取决于是否按下了向左或向右箭头。 (在游戏中我可以实现这一目标,但总是一个方向会“流动”,使玩家向某个方向滑行)。
我考虑过使用射线或其他方法来检测点击,但与之相比,它们需要更多的计算时间只是简单的方法。感谢您提供任何有关构建可扩展碰撞检测的建议和建议, 这是我在演示中进行运动和碰撞检测的基本代码:

let xVelocity = 0;
let yVelocity = 0;
var blockedMapGrid = [[0,30],[0,50],[0,100],[0,150],[0,200],[0,250],
                     [50,0],[100,0],[150,0],[200,0],[250,0],[300,0]];


var animate = function() {



if (keyState[37]) {

    xVelocity -= 1;

}
if (keyState[38]) {

    yVelocity += 1;
}
if (keyState[39]) {
    xVelocity += 1;

}
if (keyState[40]) {
    yVelocity -= 1;

}

 for (var i = 0; i < blockedMapGrid.length; i++) {
     if (Math.abs(player.position.x - blockedMapGrid[i][0]) + 
     Math.abs(player.position.y - blockedMapGrid[i][1]) < 36) {
        xVelocity = -xVelocity * 1.2;
        yVelocity = -yVelocity * 1.2;


        console.log("Blocked by " + blockedMapGrid[i][0])
    };
}


player.position.x = player.position.x + xVelocity;
player.position.y = player.position.y + yVelocity;
yVelocity *= 0.80;
xVelocity *= 0.80;

camera.position.x = player.position.x;
camera.position.y = player.position.y;

requestAnimationFrame(animate);


renderer.render(scene, camera);
};

1 个答案:

答案 0 :(得分:1)

检测器的这一部分是错误的:

Math.abs(player.position.x - blockedMapGrid[i][0]) + 
   Math.abs(player.position.y - blockedMapGrid[i][1]) < 36

基本上,在这里,您使用增加的绝对值而不是平方和的根来估算玩家到网格上点的距离。事实是,您不需要这么复杂的网格(重复的线)和距离。

您似乎正在执行Axis-Aligned Bounding Box(AABB)检测。互联网上plenty中有resources的网址是如何对其进行优化的。

但是一般方法是这样的。您的网格数组应由具有(x,y,w,h)个度量的框组成。可以是薄的,长的,正方形的任何东西。 假设您的播放器有一个边界框(player.x, player.y, player.w, player.h),然后是

for (var i = 0; i < grid.length; i++) {
   if (player.x            < grid[i].x + grid[i].w && 
       player.x + player.w > grid[i].x             &&
       player.y            < grid[i].y + grid[i].h && 
       player.y + player.h > grid[i].y) {
   //collision detected! move player to previous known position
    break;
  }
}

您可以更改检测到碰撞时的操作,但是关键是要使用4个条件查找两个框是否重叠。

更新

问题中的代码引起的另一个问题是在检测到碰撞后“弹跳”或“卡住”。

根据经验,在碰撞后切勿使用velocity = -velocity,同时也要确保角色回到“透明”状态,即玩家的边界框不与任何障碍物重叠。否则,您将陷入无限循环collision? -> vel = -vel, pos += vel*t -> collision -> ...之中,速度会从负反弹到正,然后再回弹,而不会让玩家离开墙壁。

最简单的解决方法是先在临时变量中计算玩家的新位置,检查新位置是否不冲突,然后使其永久不变并调用render(),否则只需忽略它并渲染不移动播放器。

另一种方法是记住上一个已知的“好”位置,并且只有在动画或一系列无法​​控制的动作之后,才将角色的控制权返回到先前的位置。

还有更多复杂的方法,大多数情况下都涉及某种物理模拟,以使角色反弹多个障碍,前提是控制输入不会克服惯性-考虑一下在湿滑道路上的汽车或撞到多棵树的船。但是,无论哪种方式,在检测到碰撞之后并在调用“ render()”之前,您都必须将角色放置在物理上可能的位置,否则将其著名地“卡在纹理中”。