2017/11/16 補充:修正計算錯誤。
 
 
遊戲中經常會遇到這個問題,就是希望人物可以面向某一個點,而這個點可能是玩家滑鼠的位置,或是 NPC 位置。
所以這邊會說明如何使用兩個座標,就能夠取得角度。
 
我個人喜歡由下面開始計算角度,而本範例的計算方式也是如此

image01.jpg

 
 
以下為取得兩點間角度的方法,需要的人可以直接複製到專案裡使用,該方法是經過多次簡化而得出的,回傳角度為 -180 ~ 180 度。
 
// 該方法接收 a 點與 b 點, a 點為物件位置, b 點為目標位置
public double GetAngle(Vector2 a, Vector2 b)
{
    // 這邊需要過濾掉位置相同的問題
    if ( a.x == b.x && a.y >= b.y ) return 0;

    b -= a;
    double angle = Math.Acos(-b.y / b.magnitude) * (180 / Math.PI);

    return (b.x < 0 ? -angle : angle);
}
 
 
 
本範例是參考該公式而產生出來的

image02.jpg

 
 
計算邏輯為,已知 A、B 兩點,所以我使用 A 點去創造 C 點,這樣就有三個點 A、B、C,就可以求角 CAB 的角度了。
而 C 點該如何創造呢?簡單的說,就是 A 點的 Y - 1 就可以了

image03.jpg

 
 
這是一開始的方法,不過因為 C、B 點都不能夠為 (0, 0),所以以下方法會在特定的一些位置上計算錯誤。
PS:若 A 點為 (0, 1),則創造出來的 C 點就會為 (0, 0) 了,這邊特別解釋下。
PS:b.magnitude 意思就是 ((b.x * b.x) + (b.y * b.y)) 再開根號。
 
private double GetAngle(Vector2 a, Vector2 b)
{
    // 這邊需要過濾掉位置相同的問題
    if ( a.x == b.x && a.y >= b.y ) return 0;

    Vector2 c = new Vector2(a.x, a.y - 1);

    double d1 = (c.x * b.x) + (c.y * b.y);
    double d2 = c.magnitude * b.magnitude;
    double angle = Math.Acos(d1 / d2) * (180 / Math.PI);

    if ( b.x < a.x ) angle *= -1;

    return angle;
}
 
雖然上面的作法只需要加上一些判斷,就可以避開某些位置會計算錯誤的問題,但我不喜歡為了一個簡單的功能,而加入一大堆判斷式。
經過多次測試後,我發現一個新的方法,只要 A 點座標為 (0, 0),且 A、B 兩點座標位置不相同,那計算上就不會有問題了!
 
所以每次計算時,我都會把 A 點座標歸 0,簡單的說,就是把三角形移回去原點。
而詳細計算方法為 A、B、C 點座標各減去 A 點座標

image04.jpg

 
 
所以新的方法就誕生了,該方法就是將 A 點座標歸 0,B、C 點座標減去 A 點座標,而該方法就不會有特定位置會錯誤的問題了!
 
private double GetAngle(Vector2 a, Vector2 b)
{
    // 這邊需要過濾掉位置相同的問題
    if ( a.x == b.x && a.y >= b.y ) return 0;

    b -= a;
    Vector2 c = new Vector2(0, -1);

    double d1 = (c.x * b.x) + (c.y * b.y);
    double d2 = c.magnitude * b.magnitude;
    double angle = Math.Acos(d1 / d2) * (180 / Math.PI);

    if ( b.x < 0 ) angle *= -1;

    return angle;
}
 
但是我沒有因此而滿足,因為我嗅到了一點點方法可優化的味道!
大家一定有發現,該方法的 A 點座標一定會是 (0, 0),而 C 點座標一定會是 (0, -1)。
所以 double d1 = (c.x * b.x) + (c.y * b.y) = (0 * b.x) + (-1 * b.y); 
double d1 一定會是 -b.y;,不管 A、B 點座標為何,d1 結果一定是 -b.y。
 
而 c.magnitude 一定為 1,所以 double d2 一定會是 b.magnitude; ,不管 A、B 點座標為何,d2 結果一定是 b.magnitude。
所以 b1 與 b2 的計算式就可以省略了,因為我們知道它得結果為何。
 
 
以下方法就是拿掉多餘計算式後的結果
 
private double GetAngle(Vector2 a, Vector2 b)
{
    if ( a.x == b.x && a.y >= b.y ) return 0;

    b -= a;
    double angle = Math.Acos(-b.y / b.magnitude) * (180 / Math.PI);

    return (b.x < 0 ? -angle : angle);
}
 

 

範例下載:https://drive.google.com/file/d/0B0QPre3qvuw-cUNHMDRUS0MzLUU/view?usp=sharing

 

arrow
arrow

    岳 發表在 痞客邦 留言(0) 人氣()