close
2017/11/16 補充:修正計算錯誤。
遊戲中經常會遇到這個問題,就是希望人物可以面向某一個點,而這個點可能是玩家滑鼠的位置,或是 NPC 位置。
所以這邊會說明如何使用兩個座標,就能夠取得角度。
我個人喜歡由下面開始計算角度,而本範例的計算方式也是如此
以下為取得兩點間角度的方法,需要的人可以直接複製到專案裡使用,該方法是經過多次簡化而得出的,回傳角度為 -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); }
本範例是參考該公式而產生出來的
計算邏輯為,已知 A、B 兩點,所以我使用 A 點去創造 C 點,這樣就有三個點 A、B、C,就可以求角 CAB 的角度了。
而 C 點該如何創造呢?簡單的說,就是 A 點的 Y - 1 就可以了
這是一開始的方法,不過因為 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 點座標
所以新的方法就誕生了,該方法就是將 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
文章標籤
全站熱搜