3. 使用反正切来围绕某个位置转向

3.They’ve got atan, You Want atan2: using inverse tangent to turn towards a location

This post is about turning towards a point, or: more uses for trigonometry (sine, cosine, tangent, etc).

这篇帖子讨论如何定点转向:三角学的进一步应用(正弦、余弦、正切等等)。

A common feature in games is the capability to gradually move towards a given position. You may want some AI enemies that chase the player, or a character that moves towards the mouse cursor. Today we’re going to build a quick game with a cat that chases a laser pointer (which will be controlled by the mouse — the computer peripheral, that is!). How do you accomplish this? The two steps are firstly, calculating the angle at which to move (the subject of this post) and, secondly, moving progressively at that angle (covered in our previous post).

游戏中的一个常见特点是逐渐地朝着某个给定的点移动。你或许希望一些具有智能的敌人追逐玩家,或者某个角色朝着鼠标光标所在的位置移动。今天我们准备创建小猫追逐激光发射点的简单游戏(使用鼠标进行操作)。那么怎样完成呢?这包含两个步骤,首先是计算移动方向的角度,其次是朝着该角度逐步地移动。

So we want to calculate the direction needed to aim at a given point. A good place to start with any geometry problems like this is to draw a sketch. Here’s the problem:

因此我们希望针对一个给定点来计算所需的方向。开始着手处理类似这样的一些几何问题时可以考虑绘制一个草图。该问题如图:

 

As noted in our previous example, the X-Y axes form a right-angle, so we always have a right-angled triangle in these problems. Last time, we knew the angle and one side, and we wanted to find the other two sides, so we used sine and cosine. This time, we know two of the sides (the horizontal and vertical sides, which are the X and Y distances between us and our target), but we want to know the angle. To do this, we can use the tangent function.Wikipedia tells us:

正如我们前一节的例子所提到的,x轴和y轴形成一个直角,因此在这样的问题中我们始终可以得到一个直角三角形。上一次我们已知角度值和一条边的值,希望得到其他两边的值,于是我们使用sin和cos。这次我们已知两条边的值(水平边和垂直边的值,即鼠标光标处与目标距离的x轴和y轴的分量值),但是我们希望得到角度值。为了达到目的,我们可以使用tan函数,公式如下:

\tan \alpha = \displaystyle\frac{\textrm{opposite}}{\textrm{adjacent}}

Recall from last time that adjacent and opposite refer to the lengths of the sides that are adjacent and opposite to the angle of interest. So the adjacent is the horizontal distance, and the opposite is the vertical distance. In our example, we have:

回顾上次提到的内容,邻边和对边的值分别表示与斜角相邻以及相对的那条边的长度。于是邻边代表水平距离,而对边代表垂直距离。在我们的例子中,有如下公式:

\tan(\textrm{angle}) = \displaystyle\frac{\textrm{pointerY} - \textrm{catY}}{\textrm{pointerX} - \textrm{catX}}

We need to do something to both sides of the equation (standard mathematical operating procedure!) to help us work out the angle: we need to take the inverse tangent of both sides. This is called inverse tangent, or arctangent, and we’ll write it as arctan:

我们需要在公式两边做一些调整(标准的数学操作过程)以便于求出角度值:我们在两边对正切进行反向操作。这被称作反正切,我们将使用arctan书写公式:

\arctan(\tan(\textrm{angle})) = \arctan\left(\displaystyle\frac{\textrm{pointerY} - \textrm{catY}}{\textrm{pointerX} - \textrm{catX}}\right)

The left-hand side simplifies, because taking the arctangent of a tangent cancels out (by definition). So we have:

公式左边被简化了,因为执行arctan抵消了tan(由定义可知)。于是得到公式:

\textrm{angle} = \arctan\left(\displaystyle\frac{\textrm{pointerY} - \textrm{catY}}{\textrm{pointerX} - \textrm{catX}}\right)

So that tells us how to get the angle. But there’s a complication once we come to program this. You might be tempted to do this for your cat:

以上告诉我们怎样求得角度值。但是一旦编写程序去实现则会遇到一些麻烦。你或许会尝试为小猫编写如下代码:

double distX = pointer.getX() - getX();
double distY = pointer.getY() - getY();
            
double angleRadians = Math.atan(distY / distX); 

That looks fine. But here’s a useful mathematical programming tip for you:whenever you use the division operator, /, always think “can the divisor [number on the right-hand side of the division operator] be zero here?” And if that number can be zero, you will be in trouble! That is the case here. In fact, there are multiple problems with using atan (division by zero, and a problem with quadrants), which I’m not going to explain at length in this post. The short version is, in programming,almost never use atan. There’s almost always a function called atan2, which you should use instead as it circumvents the problems with atan. The atan2 function takes the X and Y distances as separate parameters — in Java, this takes the Y coordinate as the first parameter, and the X coordinate as the second, like so:

这样看上去很好。但是告诉你一个有用的数学程序设计技巧:每当你使用除法操作符“/”时,始终要考虑“除数可能为零吗?”,如何是的话,你将会遇到麻烦!我们现在便遇到这样的情况。事实上,使用atan方法会遇到很多问题(除数为零,角度为直角的问题),在这片帖子里我不准备详细讨论。简而言之,在程序设计中几乎从不使用atan方法。而我们始终使用atan2方法来代替,它能避免atan方法的问题。atan2方法将x轴和y轴分量作为单独的参数——在java中,y坐标值作为第一个参数,而x坐标值作为第二个参数,就像这样:

int distX = pointer.getX() - getX();
int distY = pointer.getY() - getY();
double angleRadians = Math.atan2(distY, distX);
int angleDegrees = (int)Math.toDegrees(angleRadians);
                        
setRotation(angleDegrees);

We have to do a little juggling at the end to convert the radians back into degrees, but that’s the core code done. I’ve filled in the rest of the scenario to make it playable: you canplay with the scenario online, or you can download it and look at the source code in Greenfoot. The laser pointer has some very simple code that moves to the last known mouse position, and the cat turns towards the location of the laser pointer and then moves towards it.

程序最后部分将弧度转换为角度时我们耍了一点小聪明,但是核心的代码还是编写完成了。我在游戏剧本的其余部分加入了适当的代码以便它能够玩起来:你可以在官网试玩该游戏剧本,或者下载到Greenfoot中查看源代码。激光发射点类中只有极少量的代码,使得它移动到最近一次鼠标点击的位置,而小猫则朝着激光发射器的方向转动并朝着它的方向移动。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值