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 篇原創文章 · 獲贊 1 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章