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

3
在这个CodePen演示中,您可以使用箭头移动“玩家”方块,在空格键上放置灯光,并且应该通过被推到相反方向来防止从任何方向越过蓝线。 “玩家”使用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个回答

2
你探测器的这部分是错误的:
Math.abs(player.position.x - blockedMapGrid[i][0]) + 
   Math.abs(player.position.y - blockedMapGrid[i][1]) < 36

基本上,在这里,您使用添加的绝对值而不是平方和的根来近似从玩家到网格上点的距离。事实是,您不需要这样一个复杂的网格(重复线条)和距离。
看起来您正在进行轴对齐边界框(AABB)检测。互联网上有很多关于如何优化它的资源。
但是一般的方法应该是这样的。您的网格数组应该由具有(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()”之前,您都必须将角色放置在一个物理上可能的位置,否则它将被称为“卡在纹理中”。

谢谢您的评论 - 我考虑使用半径点,因为它在数组中占用的空间较少。但我猜自定义形状的矩形可以弥补差异。<br>您的方法有效,尽管我遇到了“矩形内滑动”错误。当您靠近墙壁并同时按相反方向箭头时,玩家会进入框中,并在阻塞区域几乎以0速度移动。这是常见的吗?我可以通过将反弹速度略微提高来修复它,但不确定这是否是最好的方法。 - Sanchez Panza
@SanchezPanza 我会更新一个答案来帮助解决弹跳问题。 - Alex Pakka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接