30.在墙壁间反弹

来源:互联网 发布:百度省市区数据库 编辑:程序博客网 时间:2024/04/28 12:00

30.Bouncing Off The Walls

In this post we will continue building our pool game. One of the aspects of a pool game that we will need is the ability for the balls to bounce off the edges/cushions of the table (which I’ll refer to as walls).

在这篇帖子里我们将继续创建我们的桌球游戏。我们需要实现的桌球游戏的一方面内容是能够让桌球在球台的库边(我将其看做墙壁)之间反弹。

Simple Bouncing

简单的反弹

If a wall is horizontal or vertical, there is a very simple way to implement bouncing off it, which many programmers figure out quite quickly. If you want to bounce off a horizontal wall (e.g. top or bottom of the screen), simply reverse your Y velocity. If you want to bounce off a vertical wall (e.g. left or right edge) then reverse your X velocity. These are actually two specific cases of the more general problem of bouncing off an arbitrarily-angled wall.

如果一个墙壁是水平或垂直的,这儿有一个许多编程者能够极快解决的简单方法来实现反弹。如果你希望对水平墙壁反弹(比如屏幕的上端和下端),可简单的将Y方向速度取反。如果你希望对垂直墙壁反弹(比如左边缘或右边缘)则将X方向速度取反。这实际上是两个特例,更一般的问题是对任意角度的墙壁进行反弹。

Any Which Way But Loose

任意的反弹

The basic principle when bouncing is that the angle at which you hit the wall (orange line) should be mirrored when you bounce off (blue line):

反弹的基本原理是撞击墙壁时的角度(橘色线)应该按镜像方式(蓝色线)反弹出去:

The dotted line protruding perpendicularly from the wall, which acts as the mirror, is called the surface “normal”. Let’s rotate the wall and incoming angle and work out what the outgoing angle should be:

从墙壁上垂直延伸出来的作为镜子的虚线被称为平面“法线”。让我们旋转墙壁和入射角并计算出射角应该是多少:

So, we have the incoming angle (which is calculated using the start of the incoming orange line, at the top left). We will assume we have the angle of the normal. What we want to know is the outgoing angle. If we label the gap between the incoming/outgoing as “diff”, then it’s fairly clear that:

于是,我们得到入射角(计算时使用橘色入射直线的起点,在左上角)。我们将假设我们知道法线的角度。我们希望知道的是出射角。如果我们将入射角/出射角之前的空白标记为“diff ”,则相当清楚地得到:

\text{outgoing} = \text{normal} - \text{diff}

So, how do we work out diff? Well, we can calculate the angle at the end of the incoming arrow quite simply: it’s 180 degrees away from incoming. Then we can see by looking at the point of impact that:

那么,如果计算出diff值?好吧,我们可以非常简单地计算入射箭头末端的角度:它与入射角相差180度。接着我们通过观察撞击点可以知道:

\text{diff} = (180 + \text{incoming}) - \text{normal}

So overall, expanding this out:

因此总的来说,展开公式得到:

\text{outgoing} = 2 \times \text{normal} - 180 - \text{incoming}

It’s a simple matter to implement straight-line walls that perform this collision resolution by bouncing balls using the normal angle:

要去实现竖直墙壁所执行的这种通过使用法线角度反弹桌球的碰撞解决方案,是一个简单的事情:

        for (Wall w : (List<Wall>)getObjects(Wall.class))        {                    if (w.intersectsCircle((int)newX, (int)newY, b.getRadius()))            {                double angle = Math.toDegrees(Math.atan2(vy, vx));                int normalAngle = w.getNormalAngle((int)newX, (int)newY, b.getRadius());                angle = 2 * normalAngle - 180 - angle;                double mag = 0.9 * Math.hypot(vx, vy);                                vx = Math.cos(Math.toRadians(angle)) * mag;                vy = Math.sin(Math.toRadians(angle)) * mag;            }        }

Jaws

拐角

The cushions on a pool table are not solely straight-lines, however. Next to the pockets, you have rounded sections: the jaws. I’m going to conceive of these sections as quarter-circles, as that makes the maths more straightforward. So we might have a situation like this, where the ball should bounce off the jaws:

然而,桌球台的库边不是绝对竖直的。靠近袋口处,你会遇到圆形的区域:拐角。我打算将这些区域看作是四分之一个圆,这使得数学计算更明确。于是我们可能遇到这样一种情形,这儿桌球会碰到拐角后反弹:

We can use the same calculation for resolving bounces as before — we just need a surface normal angle. Well, the surface normal for a circle is actually trivial: it always points away from the centre of the circle, you just have to work out where you hit the edge. So using our previous diagram, the normal angle is just the angle pointing from the centre of the jaw quarter-circle towards the centre of the ball:

我们可以使用与之前解决反弹相同的计算方法——我们仅仅需要一个平面法线角度值。其实,圆的平面法线实际上是很平常的:它始终从圆心指出,你仅需要算出桌球与库边的撞击位置。于是使用我们前面的示意图,法线角度正好是从拐角处四分之一圆的中心指向桌球中心的角度。

Pocketed

袋口

I’ve added some pockets to the scenario using some very simple maths: a ball goes in the pocket if its centre is over the pocket (not merely if part of the ball is over the pocket). You can have a play withthe live scenario on the Greenfoot site — the cue ball heads towards the mouse when you click it. I may need to adjust the size of the pockets though, it may end up a bit hard to pocket anything!

我已经在剧本中添加了一些袋口,这使用了一些非常简单的数学知识:一个桌球掉入袋口当它的中心越过了袋口(不仅仅是桌球部分越过了袋口)。你可以在Greenfoot网站上在线玩一下游戏剧本——当你点击鼠标时母球朝着点击处前进。然而我也许需要调整一下袋口的大小,它可能最终让桌球掉进去有一点困难。

原创粉丝点击