2018年10月13日 星期六

Unity上常用的基本向量計算 (Vector)


甚麼是Vector(向量)?簡單來說就是幾個代表了各自維度的數字的組合,例如2D世界的Vector就是(x,y)兩個數字,3D世界的Vector就是(x,y,z)。不過比起這些數字上組合,數學上對待Vector比較重視其magnitude(模)和direction(方向)這兩個特點的組合。

在一個遊戲世界的coordinates中,一個遊戲物件通常會有位置方位,而這兩個要素都可以分別用Vector表達。在Unity中寫程式時,假如是2D遊戲的話,就用Vector2這個datatype來表達,3D遊戲則是用Vector3。雖然位置看起來似乎並不是一個向量(因為它不是方向),但你可以想像它是由原點(0,0,0)指著遊戲物件所在的位置,而Unity為了方便計算所以統一把位置和方向都用Vector來表達,然後提供了許多方便你做Vector計算的函數庫。不過要留意之後在做Matrix Transformation的時候,對於位置Vector和方向Vector的處理方式會不太一樣。

Magnitude (模)

Vector的magnitude其實就是它的長度。要計算兩個物件之間的直線距離的話,就要相減它們的位置(先後次序無所謂),得出一個Vector(代表從其中一個物件指向另外一個物件),再計算這個Vector的magnitude。

Vector V的magnitude的計算方法: |V| = sqrt(Vx^2 + Vy^2 + Vz^2)  (如果是2D就不用加Vz^2,之後的公式也是)

其實這個公式就是來自勾股定理(Pythagorean theorem),也能看得出Magnitude不會有負數。

Unit Vector/Normalized Vector (單位向量)

Unit vector(又叫Normalized vector)是指magnitude只有1的vector,可以說這個unit vector只有direction而沒有magnitude。只要把一個Vector內的每個值除以它的magnitude,就能得到Unit vector了,這個過程叫做Normalization(歸一化)。Unit vector是其中一種表達物件方向的方法(尤其在3D世界內難以用角度來表達方向)。

要判斷一個vector是否unit vector,最快的方法是看其和自己的dot product是否1(下面會詳細講解)。

Dot Product (點積)

Dot product的計算方法: A.B = AxBx + AyBy + AzBz
可以看得出兩個Vector的Dot product只是一個數而非Vector。

一個常用的角度公式:
A.B = |A| |B| cos (A和B的夾角)

Dot product的常見用途:

計算投影

一個Vector A和一個Unit Vector B的Dot product代表了A在B這個方向上的投影的長度。

另外,一個Unit vector和自己的dot product是1(可以想像兩個一模一樣的vector出來的投影肯定也和自己一樣),因此要判斷一個vector是否unit vector最快的方法是計算dot product是否1(而非使用浪費時間的sqrt來計算vector的magnitude)。

比較方向

如果兩個Vector的Dot product大於0,代表這兩個Vector間的角度小於90度(我稱為指著大致相同方向)。
如果等於0,代表兩個Vector互相垂直。
如果小於0,代表兩個Vector的角度大於90度(我稱之為相反方向)。

判斷視線範圍

在很多遊戲中,一個怪物會有牠的視線範圍,當你步入了這個範圍中牠就會攻擊你。一個常用的視線範圍判定方法是:以怪物面向的方向為一個Vector(這個Vector的長度代表了怪物能觀察的距離),然後設定觀察角度(即怪物只能看到Vector左邊角度除以2和右邊角度除以2內的物件)。

那麼要如何判定玩家是否在怪物的視線範圍內?條件為以下兩項同時符合:

  • 觀察距離:玩家和怪物間的距離 <= 怪物面向Vector的magnitude
  • 觀察角度:怪物指著玩家的Vector 與 怪物面向Vector間的夾角 <= 觀察角度/2
如何計算上述提到的夾角?可以利用上面提到過的角度公式:

設「怪物指著玩家的Vector」為A,「怪物面向Vector」為B
(留意A的magnitude其實就是玩家和怪物間的距離)
A.B = |A| |B| cos (A和B間的夾角)
夾角 = arccos (  (A.B) / (|A| |B|)   )

另外要注意,怪物指著玩家的Vector是 玩家的位置 - 怪物的位置 (而非相反)
(如果你覺得很容易搞混相減的次序,試想想從怪物指著玩家,即是怪物是原點,這樣就知道是要玩家減怪物了)

Cross Product (叉積)

兩個Vector的Cross product是一個新的Vector,而非像dot product般只是一個數。

Cross product的計算方法:A X B = (AyBz - AzBy, AzBx - AxBz, AxBy - AyBx)
(尤其注意y點是先z後x,而非先x後z)

另外,A X B = -(B X A)
(從公式就能看得出,因為減法次序的改變,所以A X B和B X A的關係是正負相反)

一個常用的角度公式:
|A X B| = |A| |B| sin (A和B的夾角)

Cross product的用途:

3D遊戲:計算垂直Vector

在3D遊戲的情況下,A X B得出來的Vector會同時和A和B垂直(若A和B平行的話會得出0,0,0)。
能和A和B同時垂直的方向有兩邊,那麼怎麼知道這個Vector是指著哪邊?
Unity內的Scene View用的是Left-hand rule,在Left-hand rule下A是拇指、B是食指的話,A X B就是中指指著的方向。

若你想要的是相反方向的垂直Vector,使用B X A計算或者直接用-(A X B)即可。

假如我們需要知道3D世界內一個三角形的平面對著哪個方向,可以用三角形的三個邊的其中兩個作為Vector,便能計算出與這個平面垂直的Vector。

2D遊戲:計算面積

在2D世界的情況下,A X B得出來的Vector的magnitude是A和B形成的平行四邊形的面積,除以二的話就是三角形的面積。

沒有留言:

張貼留言