如何计算弹跳角?

69

我花了一些时间来尝试,但我仍然无法理解。

我制作了一个可以发射导弹的坦克,当导弹击中墙壁时,我想让它们弹开,但我希望它们能够弹到正确的角度。

目前我还没有遇到任何障碍,当导弹超出我创建的viewportRectangle视口矩形时,它们就会弹回。

我正在寻找的解决方案是否相当高级?

有没有相对简单的方法可以实现?


2
如果你去打一局台球,这个问题的解决方案可能会立即变得显而易见。 - smartcaveman
10个回答

205
你可能认为,因为你的墙与坐标轴对齐,所以编写特殊情况代码是有意义的(对于垂直墙壁,取速度的x坐标的相反数;对于水平墙壁,取速度的y坐标的相反数)。然而,一旦你已经成功地处理了垂直和水平墙壁,你可能会想到下一个问题是,“那么任意角度的墙壁呢?”因此从一开始就考虑一般情况是值得的。
在一般情况下,假设你的导弹具有速度v,并且撞到了表面法线为n的墙壁。

Missile with vector v about to obliquely hit a wall with surface normal n.

v分解为垂直于墙面的分量u和平行于墙面的分量w

Right-angled triangle with hypotenuse for v, short side u parallel to wall and long side w parallel to wall.

在此处:

u = (v · n / n · n) n
w = vu

这里,v · n 是向量 vn点积。请参见链接以了解如何计算它。点积 n · n 评估法线向量长度的平方; 如果您始终保持法线向量为单位向量形式,则 n · n = 1,您可以省略除法。

弹跳后,平行于墙壁运动的分量受到摩擦力 f 的影响,垂直于墙壁的分量受到弹性的影响,可以用恢复系数r来表示。

所以碰撞后的速度为v' = fw - ru。在完全弹性、无摩擦的碰撞中,v' = w - u;也就是说,在碰撞点处,运动沿法线反射,就像Bill的答案中给出的图示一样。这种方法在三维中同样适用。
(显然,这是一个非常简化的反弹概念;它没有考虑到角动量或变形。但对于许多种类的视频游戏来说,这种简化是完全足够的。)

3
我同意从为竖直和水平墙编写特例代码入手是错误的方式。这不是通向正确路线的垫脚石,而是一条死路。 - Adam Kane
嘿,大家好,这是一个很棒的解释,但我认为它有一个错误。难道不应该是u = -n ( v.n / n.n )吗?毕竟,u是在n的反向方向上。 - nullspace
2
我是不是唯一一个对使用v.n感到困惑的人,而不是使用v·n?当我读到n.n时,我的大脑就会爆炸;认为一个正常的元素叫做normal(我太习惯C++了吗?)。 - Rookie
计算表面法线n,请参考此问题:https://dev59.com/FnM_5IYBdhLWcg3ww2AQ - user5157912
太棒了,3D中也像魔法一样运行! - Will Lacey
显示剩余4条评论

72
我认为更简单的方法是使用导弹的速度而不是计算角度。假设你有一枚导弹,它具有 xVelocityyVelocity 以表示它的水平和垂直运动。这些速度可以是正数或负数,以表示向左、向右、向上或向下。
  • 如果导弹击中顶部或底部边界,请反转yVelocity 的符号。
  • 如果导弹击中左侧或右侧边界,请反转xVelocity 的符号。
这将保持相反轴的移动不变。
借用自ChrisF的答案的图片,假设导弹从位置I开始。

反射角

xVelocityyVelocity 均为正数时(在2D图形中右和下通常为正数),导弹将沿所示方向移动。让我们假定赋值为:
xVelocity = 3
yVelocity = 4

当导弹击中位置为C的墙壁时,它的xVelocity不应该改变,但是它的yVelocity应该被反转为-4,这样它就会向上方反弹,但仍然朝右移动。

使用这种方法的好处是,您只需要跟踪导弹的xPositionyPositionxVelocityyVelocity。使用这四个组件和游戏的更新速率,导弹将始终以正确的位置重绘。一旦涉及到不在直角或移动的更复杂的障碍物,使用X和Y速度比使用角度更容易处理。


35
只有当你所有的墙都与坐标轴对齐时,它才能起作用,即使如此,你仍然需要为垂直和水平墙分别考虑情况。在一般情况下,可能更容易做好这个问题。 - Gareth Rees
1
不,这并不是。在一般情况下,你需要使用一些三角学来对齐你的向量,使得墙面在计算时变为水平或垂直状态,然后进行计算,并将其转换回原始状态。这只需要2-4次三角函数计算而已,但并不更简单。 - Karl
@Gareth:你说得对,这只适用于边框和与边框对齐的墙壁。我只是回答了被问到的问题,这比一般情况容易得多。 - Bill the Lizard
1
@Karl:不需要三角学! - Gareth Rees
1
当然,你可以轻松地将其适应于不同角度的静止或移动物体。只需使用碰撞中两个物体的速度来计算每个物体的新速度即可。 - Bill the Lizard
显示剩余2条评论

8

我曾经遇到过这个问题,唯一的解决方法是分离碰撞轴!

试一下:

x += velocity * Math.cos(angle * Math.PI /180);
y += velocity * Math.sin(angle * Math.PI /180);

if (x < 0 || x > canvas.width) {
     angle = 180 - angle;   
}
else if (y < 0 ||y > canvas.height) {
     angle = 360 - angle; 
}

我希望这可以帮助你!

不是理想的通用解决方案,但它正是我现在所需要的(反射画布边缘)。由于某种原因,我的大脑无法独立想出这个解决方案。 - Draco18s no longer trusts SE

8

对于完美的粒子(和光),反射角等于入射角,如这个图示所示(来自commons.wikimedia.org)。

Angle of Reflection

反射的维基百科页面 很好地解释了它的工作原理。

当考虑到物体和障碍物的弹性和材料时,这会变得有点复杂,但对于大多数应用程序来说,这可能已经足够了。


6
作为对你所问的具体物理问题的补充,我建议你阅读Wendy Stahler的书《面向游戏程序员的初级数学和物理学》。我发现它对我的游戏/物理编程项目非常有用。
该书附带的代码是C ++,但如果你懂C#,那么进行转换会很容易。
祝一切顺利!

4

180-a并非在所有情况下都有效,除非您只是在顶部表面上弹跳时X增加。

一个方向是前往XNA论坛或获取XNA示例代码。它是用于构建游戏的C#。我并不是说您想在XNA中构建游戏,但它是一个很好的工具,而且是免费的。


我正在使用XNA。 是的,出现了问题。 当在窗口的左右边界发射导弹时,导弹会正确反射,但当在窗口的底部或顶部边界射击时,我可以看到反射,只是角度不正确。 - Moulde

3

a = 2w - b

其中:
a => 结果角度
w => 墙壁或地板或天花板的角度
b => 球的角度

这是我尝试寻找最简单的公式来计算球在墙壁、天花板和地板上反弹后的结果角度。结果可能超过+360度或-360度,但它们仍然是等效的角度。

例如,如果天花板角度为270度,球的角度为30度,则结果角度为510度,相当于+150度或-210度。如果您将天花板角度改为90度而不是270度,则结果仍为150度。


这个答案的优雅和清晰度被低估了。 - ClickRick

1

非常简单 - 伪代码:

angleObjectHitWall = a;
bounceAngle = 180-a;

当然,这只是一个非常简单的计算,一旦考虑到诸如材料、重力、不直的墙壁等因素,它就完全不相关了...


5
不行,这只适用于入射角和反射角相等的情况。 - bergin
@bergin - 对于游戏中的理想粒子来说,这几乎总是如此。 - Davor

0

这实际上是一个物理问题,所以如果你不是物理学家(而且由于你提出了这个问题,我会认为你不是),需要大量阅读和头脑风暴才能得到正确的答案。

我建议阅读this维基百科条目,以了解你的问题的深度。

如果你只想让它“看起来可信”,那么我不会太担心它,可以使用Bill the Lizard的答案,但是如果你想做对,你将会有一次相当的冒险。不要被吓到!祝你好运!


1
除非他正在编写物理模拟引擎,否则这与物理无关。OP只是想要简单的镜像反射。 - Davor

-5
if(!Collide(Missle, Mainchar)){
(Velocity.x)*-1;
(Velocity.y)*-1;
}

它能够正常工作且简单易用,祝你好运。


4
没错,这会使物体沿着原始向量反向返回。这不是镜像反射。 - Draco18s no longer trusts SE

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