26.激光切割机:不放过任何目标;在直线和圆之间进行碰撞检测

来源:互联网 发布:弹性棉条绒裤子淘宝 编辑:程序博客网 时间:2024/04/27 17:11

26.Laser Cutter: Don’t Miss A Thing; collision detection between a line and a circle

In our last post, we saw how to do collision detection between bullets (modelled as an exact point) and asteroids (modelled as a circle). In this post we are going to upgrade our spaceship, and equip it with a laser for breaking up asteroids. This is going to involve a bit of equation re-arranging.

在我们上一篇帖子里,我们了解了如何在子弹(描述为一个精确的点)和行星(描述为一个圆)之间执行碰撞检测。这篇帖子我们准备升级我们的飞船,并给它装备一个激光放射器来击碎行星。这将涉及到一点等式变形。

Space Lasers

太空激光发射器

A laser is a straight line projecting from the front of the spaceship. To do collision detection between the laser and the asteroid, we need to check whether a line intersects a circle. For that, we return tothe mathematical representation of a line, representing our laser line as:

一束激光是从飞船前面发射出的一条直线。为了在激光和行星之间进行碰撞检测,我们需要检查一条直线是否与圆相交。为了到达目的,我们回忆一下直线的数学表示形式,将我们的激光直线表示为:

\begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} \text{spaceshipX} \\ \text{spaceshipY} \end{pmatrix} + \text{scalar} \times \begin{pmatrix} \text{dirX} \\ \text{dirY} \end{pmatrix}

We know from last time that the way to check if a particular point intersects a circle is to check the distance against the radius:

我们从上篇帖子得知检查一个特殊点和一个圆是否相交的方式便是检查点与圆心的距离:

\sqrt{(x - \text{centreX})^2 + (y - \text{centreY})^2} \leq \text{radius}

To find out if any point on the line is inside the circle, we can actually subsitute our line equation into the circle equation, like so:

为了确定直线上的某些点是否在圆的内部,我们实际上可以将直线方程带入到圆的方程中,像这样:

\sqrt{(\text{spaceshipX} + (\text{scalar}\times\text{dirX}) - \text{centreX})^2 + (\text{spaceshipY} + (\text{scalar}\times\text{dirY}) - \text{centreY})^2} \leq \text{radius}

This will tell us if any point on the line is inside the circle. In fact, for collision detection, all we need to check is if the line hits the outside of the circle. If the line touches the circle at all, we know that they intersect. Mathematically, we can change that less-than-or-equal-to into an equals (this will come in handy later on):

这将告诉我们直线上的某些点是否在圆的内部。事实上,为了碰撞检测,所有我们需要做的就是检查直线是否碰撞到了圆的外围。只要直线接触到了圆,我们便知道它们相交了。在数学上,我们可以把小于等于转变为等于(后面将用到这个):

\sqrt{(\text{spaceshipX} + (\text{scalar}\times\text{dirX}) - \text{centreX})^2 + (\text{spaceshipY} + (\text{scalar}\times\text{dirY}) - \text{centreY})^2} = \text{radius}

The only unknown in that equation is scalar, so we need to rearrange for that — we can start by squaring both sides, because we know both sides will be positive:

上面等式中唯一未知的便是scalar,因此我们需要重新排列——我们可以首先去掉平方根,因为我们知道等式两边都是为正的:

(\text{spaceshipX} + (\text{scalar}\times\text{dirX}) - \text{centreX})^2 + (\text{spaceshipY} + (\text{scalar}\times\text{dirY}) - \text{centreY})^2 = \text{radius}^2

Let’s define a couple of helper variables to trim that down:

让我们定义一对辅助变量来削减等式:

\text{distX} = \text{spaceshipX} - \text{centreX}
\text{distY} = \text{spaceshipY} - \text{centreY}

That gives us:

于是我们得到:

(\text{distX} + (\text{scalar}\times\text{dirX}))^2 + (\text{distY} + (\text{scalar}\times\text{dirY}))^2 = \text{radius}^2

\text{distX}^2 + (2\times\text{distX}\times\text{scalar}\times\text{dirX}) + (\text{scalar}^2\times\text{dirX}^2) + \text{distY}^2 + (2\times\text{distY}\times\text{scalar}\times\text{dirY}) + (\text{scalar}^2\times\text{dirY}^2) = \text{radius}^2

(\text{dirX}^2 + \text{dirY}^2) \times \text{scalar}^2 + ((2\times\text{distX}\times\text{dirX}) + (2\times\text{distY}\times\text{dirY}))\times\text{scalar} + \text{distX}^2 + \text{distY}^2 - \text{radius}^2 = 0

Phew! Now what we have is a quadratic equation, which we can solve using the standardquadratic formula. The quadratic formula tells us that any equation of the formax^2 + bx + c can be rearranged to:

哎呀!现在我们所得到的是一个二次方程,它可以使用标准的二次公式来求解。二次公式告诉我们任何形如ax^2 + bx + c的方程式都可以变形为:

x = \displaystyle\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}

And in fact, we don’t care what the value of x is, we just want to know if the equation has a solution: if it has a solution, the line touches the circle — if there is no solution, that’s because the line doesn’t touch the circle. The part which tells us whether there is a solution is the b^2 - 4 \times a \times c part — if this is 0 or higher (and thus can be square-rooted) then there is a solution, but if it’s negative then there’s no solution. From above, we have that:

实际上,我们并不在意x的值是多少,我们仅仅希望知道该方程是否有解:如果有一个解,直线便会接触到圆——如果无解那是因为直线没有和圆碰触到。公式告诉我们是否有解的部分是b^2 - 4 \times a \times c——如果大于等于零(于是可以开平方)则有解,但是如果它是负数则说明无解。从上面的公式我们可以得到:

x = \text{scalar}
a = \text{dirX}^2 + \text{dirY}^2
b = (2\times\text{distX}\times\text{dirX}) + (2\times\text{distY}\times\text{dirY})
c = \text{distX}^2 + \text{distY}^2 - \text{radius}^2

And thus our code for collision detection between the laser and the asteroid is:

因而我们在激光和行星之间进行碰撞检测的代码便是:

         double dirX = Math.cos(Math.toRadians(getRotation()));        double dirY = Math.sin(Math.toRadians(getRotation()));                for (Asteroid asteroid : (List<Asteroid>)getWorld().getObjects(Asteroid.class))        {            double distX = getX() - asteroid.getX();            double distY = getY() - asteroid.getY();                        double a = dirX * dirX + dirY * dirY;            double b = 2 * distX * dirX + 2 * distY * dirY;            double c = distX * distX + distY * distY - asteroid.getRadius() * asteroid.getRadius();                        if (b * b - 4 * a * c >= 0)            {                if (asteroid.hit(30))                {                    destroyedAsteroid();                }            }        }

And do you know what — it works! You can have a play live on the Greenfoot site.

你知道发生了什么——它生效了!你可以在Greenfoot网站上现场玩一下