在游戲開發(fā)中,需要使用到向量,三角函數(shù)之類的知識。先學(xué)會里面的概念,后續(xù)應(yīng)用才會容易制作。
1.笛卡爾坐標(biāo)系
2d坐標(biāo)系:x,y
3d坐標(biāo)系:x,y,z
在3d坐標(biāo)系里面有左手坐標(biāo)系和右手坐標(biāo)系。這個可能對人來說有直觀認(rèn)知上的區(qū)別,其實是不相悖。
右手坐標(biāo)系
左手坐標(biāo)系
附帶:
極坐標(biāo)系(polar coordinates)是指在平面內(nèi)由極點(diǎn)、極軸和極徑組成的坐標(biāo)系。在平面上取定一點(diǎn)O,稱為極點(diǎn)。從O出發(fā)引一條射線Ox,稱為極軸。再取定一個單位長度,通常規(guī)定角度取逆時針方向為正。這樣,平面上任一點(diǎn)P的位置就可以用線段OP的長度ρ以及從Ox到OP的角度θ來確定,有序數(shù)對(ρ,θ)就稱為P點(diǎn)的極坐標(biāo),記為P(ρ,θ);ρ稱為P點(diǎn)的極徑,θ稱為P點(diǎn)的極角。
極坐標(biāo)系用于定位和導(dǎo)航。極坐標(biāo)通常被用于導(dǎo)航,作為旅行的目的地或方向可以作為從所考慮的物體的距離和角度。
2.三角學(xué)
1.直角三角形三角函數(shù)概念
a:對邊
b:鄰邊
c:斜邊
勾股定理(畢達(dá)哥拉斯定理)
正弦:對邊比斜邊。
余弦:鄰邊比斜邊。
正切:對邊比鄰邊。
余切:鄰邊比對邊。
2.角度弧度
半徑為1的園,全弧長為2πr。
角度是兩條線段的夾角,弧度是兩條線段和園相交的點(diǎn),在圓弧上走過的距離。
3.誘導(dǎo)公式
1.二倍角
在閱讀Detour源碼里面有這樣的應(yīng)用。里面將會讀取一個sin cos的一半做乘法。
3.向量
向量計算應(yīng)用于游戲中來計算位置,里面和三角函數(shù)也有關(guān)系。
向量和標(biāo)量不一樣,
標(biāo)量(scale)只表示數(shù)值大小;
向量(矢量、vector)包含方向和數(shù)值大小。
舉例:
速度、位移是向量
速率、長度是標(biāo)量
零向量是指的長度為0,無方向的向量。
1.加法
將兩個向量拼接成平行四邊形,對角向量就是加法的結(jié)果。兩個相同的向量相加,等于將向量長度增加一倍。
2.減法
u向量-v向量,就是指的從u向量目的點(diǎn)指向v向量目標(biāo)點(diǎn)
3.向量與標(biāo)量乘
向量與標(biāo)量乘法,將向量按照某個長度縮放,一般用于單位向量向前行進(jìn)、縮回多少距離。
4.獲取長度
獲取向量從開始到結(jié)束的距離。從向量得到標(biāo)量。利用勾股定理,向量記錄的就是直角三角形斜邊在x,y軸上的投影長度,斜邊長度就是x,y的平方和的開方。
數(shù)學(xué)公式里面向量長度使用雙豎線引用。
5.normalized
歸一化需要將向量長度計算出來,然后將向量在各個維度的分量都除以長度。這樣就能得到一個單位向量。歸一化用一條豎線。
單位化的向量分量的幾何意義
這個特性將會應(yīng)用于計算位置。
6. Dot Product
點(diǎn)乘能計算兩向量的夾角的cos值。cos有一個特點(diǎn),在取值±90°的值域都是>0。在游戲中,這種計算能很快判斷一個怪物是否在玩家身后。這個函數(shù)不能判斷左右,但是能判斷前后。
7.cross produce
叉乘用于算左右。sin有個特點(diǎn),取值在0~179°都是>0。用找個特點(diǎn)能判定向量是在自己的左邊還是右邊。
叉乘需要有3個維度才有意義。
u叉乘v之后結(jié)果是sin*u、v向量的分量。n就是垂直于u、v構(gòu)成平面的垂直法線向量。
8.獲取角度
將向量轉(zhuǎn)換成弧度,向量無需歸一化。
9.常用函數(shù)
// 將弧度轉(zhuǎn)換成角度
float radian2angle(float radian) {
return radian * (180.0f / mathfu::kPi);
}
// 將角度轉(zhuǎn)換成弧度
float angle2radian(float angle) {
return angle * (mathfu::kPi / 180.0f);
}
// 將向量轉(zhuǎn)換成角度
float vector2angle(mathfu::Vector<float, 2> a) {
return radian2angle(std::atan2(a.y, a.x));
}
// 角度轉(zhuǎn)換成向量
void angle2vector(float angle, mathfu::Vector<float, 2>& a) {
auto radian = angle2radian(angle);
a.y = std::sin(radian);
a.x = std::cos(radian);
}
// 按照角度,長度,轉(zhuǎn)換一個位置
void movepos(float angle,mathfu::Vector<float,2>& rawPos, float len, mathfu::Vector<float, 2>& out) {
mathfu::Vector<float, 2> dir;
angle2vector(angle, dir);
dir.x *= len;
dir.y *= len;
out = rawPos + dir;
}
// 輸出一個向量
void outputvector(const char* tag, mathfu::Vector<float, 2>& a) {
std::cout << tag << " "<< a.x << "," << a.y << "\n";
}
實例
1.計算圍繞role的怪物
先檢查是否和其他怪物重合
按照±小角度開始偏移嘗試是否能站
#include "mathfu/vector.h"
#include "mathfu/constants.h"
#include <iostream>
float radian2angle(float radian) {
return radian * (180.0f / mathfu::kPi);
}
float angle2radian(float angle) {
return angle * (mathfu::kPi / 180.0f);
}
float vector2angle(mathfu::Vector<float, 2> a) {
return std::atan2(a.y, a.x)*(180.0f / mathfu::kPi);
}
void angle2vector(float angle, mathfu::Vector<float, 2>& a) {
auto radian = angle2radian(angle);
a.y = std::sin(radian);
a.x = std::cos(radian);
}
void outputvector(const char* tag, mathfu::Vector<float, 2>& a) {
std::cout << tag << "=(" << a.x << "," << a.y << ")\n";
}
// rawBattleCircleFix posSelf: linmath.Vector3{X:11424.3, Y:-311.48605, Z:17336.395},
// posEnemy: linmath.Vector3{X:11330.254, Y:-311.48605, Z:17465.838},
// newPos: linmath.Vector3{X:11245.467, Y:-311.48605, Z:17601.525},
// angle: -32, e2sLen: 160.00067
// 測試怪物按照弧形排布在玩家周圍
void test_monster_battle_cricle()
{
float dst = 160.0f; // 怪物距離
float bodyRadius = 80.0f;// 怪物的寬度
mathfu::Vector<float, 2> monsterPos(11424.3f, 17336.395f);
mathfu::Vector<float, 2> rolePos(11330.254f, 17465.838f);
auto dir = monsterPos - rolePos;
auto angle = vector2angle(dir) + 20.0f;
mathfu::Vector<float, 2> finalDir;
angle2vector(angle, finalDir);
finalDir.x *= dst;
finalDir.y *= dst;
mathfu::Vector<float, 2> newPos = rolePos + finalDir;
outputvector("monsterPos", monsterPos);
outputvector("rolePos", rolePos);
outputvector("newPos",newPos);
}
cmake定義文件
cmake_minimum_required (VERSION 3.2)
project(math_base)
IF (CMAKE_SYSTEM_NAME MATCHES "Windows")
add_definitions(-DWIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_USE_MATH_DEFINES)
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Windows")
include_directories( $ENV{MATHFU_PATH}/include )
file(GLOB_RECURSE all_SRC "src/*.cpp"
"src/*.hpp" "src/*.h"
"src/*.cc" )
add_executable(test_math ${all_SRC})
target_link_libraries(test_math)
計算的位置,在坐標(biāo)系上的位置
2.計算園外切線
利用三角函數(shù)來計算點(diǎn)對于圓的切線;
void test_fun_fix_circle()
{
// 圓半徑
float radius = 8.0f;
// 圓心位置
mathfu::Vector<float, 2> circlePos(0, 0);
// 園外一點(diǎn)
mathfu::Vector<float, 2> checkPos(10, 12);
// 園外點(diǎn)指向圓心向量
mathfu::Vector<float, 2> l = circlePos - checkPos;
// 計算距離
auto len = l.Length();
// 計算出園外點(diǎn)指向圓心向量角度
float cos = radius / len;
float radian = std::acos(cos);
// 直角三角形,計算另一角度
float offsetangle = 90 - radian2angle(radian);
// 將園外點(diǎn)指向圓心向量歸一化,將向量轉(zhuǎn)換成角度
auto dir = l.Normalized();
auto oldAngle = vector2angle(l);
// 將 園外點(diǎn)指向圓心向量角度 + 通過直角三角形方式計算出來的夾角
// 這個夾角就切線方向
auto finalAngle = oldAngle + offsetangle;
// 將角度換算成為向量
mathfu::Vector<float, 2> finalDir;
angle2vector(finalAngle, finalDir);
outputvector("finalDir", finalDir);
// 計算出切線點(diǎn)離 園外點(diǎn) 的距離,將向量長度設(shè)置成這個距離
auto al = std::sqrt(len * len - radius * radius);
finalDir.x = finalDir.x * al;
finalDir.y = finalDir.y * al;
// 使用 園外點(diǎn) + 偏移向量就能得到切線過圓邊的點(diǎn)
auto finalPos = checkPos + finalDir;
outputvector("finalPos", finalPos);
}
效果:
3.計算某個點(diǎn)是否為三角形內(nèi)
原理在 b站 GAMES101-現(xiàn)代計算機(jī)圖形學(xué)入門-閆令琪 38分鐘處講解了。
叉積是用于控制左右。如果獲取的值域是正數(shù)左邊,負(fù)數(shù)為右邊。
利用的是,三角形三點(diǎn)按照順時針的向量,以及p點(diǎn)的向量的叉乘永遠(yuǎn)是相同的象限的。
void test_triangle_inner()
{
mathfu::Vector<float, 2> A(8.66992, 6.79278);
mathfu::Vector<float, 2> B(4.96974, 2.1609);
mathfu::Vector<float, 2> C(12.31686, 1.78822);
mathfu::Vector<float, 2> P(8.98936, 4.07754);
mathfu::Vector<float, 2> P2(11, 5);
auto u = B - A;
auto v = C - B;
auto w = A - C;
std::cout << "start check P\n";
auto t = P - A;
std::cout << t.DotProduct(t, u) << "\n";
t = P - B;
std::cout << t.DotProduct(t, v) << "\n";
t = P - C;
std::cout << t.DotProduct(t, w) << "\n";
std::cout << "start check P2\n";
t = P2 - A;
std::cout << t.DotProduct(t, u) << "\n";
t = P2 - B;
std::cout << t.DotProduct(t, v) << "\n";
t = P2 - C;
std::cout << t.DotProduct(t, w) << "\n";
}
//output:
//start check P
//11.3947
//28.8183
//23.5922
//start check P2
//- 0.317775
//43.247
//20.8761
// 如果旋轉(zhuǎn)方向相同,這些向量的sin值的符號都是一致的。
//
// 這個計算不會涉及到開方,所以很好用。
//static inline T DotProductHelper(const Vector<T, 2>& v1,
// const Vector<T, 2>& v2) {
// return v1[0] * v2[0] + v1[1] * v2[1];
// }
4.計算矩形內(nèi)的一點(diǎn)
原理和三角形檢查一樣。
先將一個矩形做偏移,旋轉(zhuǎn):
取兩個點(diǎn)開始計算:
void test_rect_inner()
{
mathfu::Vector<float, 2> r1(-4, -5);
mathfu::Vector<float, 2> r2(-1.401923789, -3.5);
mathfu::Vector<float, 2> r3(-4.901923789, 2.562177826);
mathfu::Vector<float, 2> r4(-7.5, 1.062177826);
mathfu::Vector<float, 2> i(-4.88, -1.49);
mathfu::Vector<float, 2> j(-8.26, -3.77);
std::cout << "計算i點(diǎn)\n";
auto t1 = r2 - r1;
auto t2 = i - r1;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1,t2) << "\n";
t1 = r3 - r2;
t2 = i - r2;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1, t2) << "\n";
t1 = r4 - r3;
t2 = i - r3;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1, t2) << "\n";
t1 = r1 - r4;
t2 = i - r4;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1, t2) << "\n";
std::cout << "計算j點(diǎn)\n";
t1 = r2 - r1;
t2 = j - r1;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1, t2) << "\n";
t1 = r3 - r2;
t2 = j - r2;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1, t2) << "\n";
t1 = r4 - r3;
t2 = j - r3;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1, t2) << "\n";
t1 = r1 - r4;
t2 = j - r4;
std::cout << mathfu::Vector<float, 2>::DotProduct(t1, t2) << "\n";
}
4.矩陣
1.概念
矩陣是按照行列方式排列的數(shù)字。是線性代數(shù)里面中重要的數(shù)學(xué)概念。
描述矩陣一般都是說
的矩陣。r是rows行(橫著的條目算1個),c是column(豎著的條目算1個)
方陣就是行和列數(shù)目都是相同的。在3d運(yùn)算中經(jīng)常使用這種方陣
單位矩陣,對角線都是1,其余都是0。
書寫的時候,矩陣都是寫成大寫。M,A,R。手寫的時候,矩陣的括號其實要寫成圓括號(),印刷體中都是[]表示。
向量轉(zhuǎn)換成矩陣可以成為 行矩陣、列矩陣。
2.矩陣運(yùn)算
單位矩陣
1.轉(zhuǎn)置
記作:
2.矩陣與標(biāo)量乘
3.矩陣乘法
公式定義:
公式分解:
1、當(dāng)矩陣A的列數(shù)(column)等于矩陣B的行數(shù)(row)時,A與B可以相乘。
2、矩陣C的行數(shù)等于矩陣A的行數(shù),C的列數(shù)等于B的列數(shù)。
3、乘積C的第m行第n列的元素等于矩陣A的第m行的元素與矩陣B的第n列對應(yīng)元素乘積之和。
使用矩陣來做位移,旋轉(zhuǎn),縮放操作:
坐標(biāo)系上的位置:
4.克羅內(nèi)克積(Kronecker Product)
克羅內(nèi)克積是兩個任意大小的矩陣間的運(yùn)算,符號記作 。克羅內(nèi)克積也被稱為直積或張量積.以德國數(shù)學(xué)家利奧波德·克羅內(nèi)克命名。
5.四元數(shù)
1.概述
四元數(shù)是1843年發(fā)明的。愛爾蘭數(shù)學(xué)家哈密頓(William Rowan Hamilton,1805-1865)。
四元數(shù)運(yùn)算在電動力學(xué)與廣義相對論中有廣泛的應(yīng)用。四元數(shù)可以用來取代張量表示。有時候采用帶有復(fù)數(shù)元素之四元數(shù)會比較容易,導(dǎo)得結(jié)果不為除法代數(shù)之形式。然而亦可結(jié)合共軛運(yùn)算以達(dá)到相同的運(yùn)算結(jié)果。
從概念上來看,就是在數(shù)學(xué)里面定義對于-1開方最后獲取的值。
復(fù)數(shù)是對實數(shù)集合的一種擴(kuò)展。
在游戲開發(fā)應(yīng)用里面,四元數(shù)用于做旋轉(zhuǎn)計算。所以最好先將矩陣搞清楚。復(fù)數(shù)已經(jīng)是一種數(shù)學(xué)工具了,在實際世界里面不能表示什么意義。
四元數(shù)不是專門給3D圖形學(xué)設(shè)計的,但是能用在3D圖形學(xué)里面:
- 3D相機(jī)控制
- 壓縮存儲
- 平滑3D插值
復(fù)數(shù)定義
a是實部,b是虛部;
復(fù)數(shù)與標(biāo)量相乘、相除
復(fù)數(shù)加減
復(fù)數(shù)加法恒等元
復(fù)數(shù)恒等元
復(fù)數(shù)除法
推算的時候,需要分子和分母都乘上分母的共軛復(fù)數(shù)。
共軛(Conjugate)
兩個實部相等,虛部互為相反數(shù)的復(fù)數(shù)互為共軛復(fù)數(shù)(conjugate complex number)。(當(dāng)虛部不等于0時也叫共軛虛數(shù))復(fù)數(shù)z的共軛復(fù)數(shù)記作 (z上加一橫,英文中可讀作Conjugate z,z conjugate or z bar),有時也可表示為
計算負(fù)數(shù)的模
中劃線
2.歐拉恒等式
我還沒有理解到這個意義。
當(dāng)
推導(dǎo)
3.歐拉角
4.萬向節(jié)死鎖
參考
- [1] markdown公式
- [2] windows10輸入公式
- [3] 三角函數(shù)
- [4] 圖形計算器
- [5] GeoGebra數(shù)學(xué)
- [6] GeoGebra-Classic
- [7] Google-mathfun庫
- [8] GAMES101-現(xiàn)代計算機(jī)圖形學(xué)入門-閆令琪
- [9] NumPy教程
- [10] 2d/3dPython庫
- [11] markdown公式2
- [12] markdown公式3
- [13] B站矩陣
- [14] 極坐標(biāo)
- [15] 3d數(shù)學(xué)筆記
- [16] 媽咪說-復(fù)數(shù)
- [17] GeoGebra-Classic-Win版本
- [18] 復(fù)數(shù)運(yùn)算規(guī)則
- [19] 歐拉角和四元數(shù)的理解
- [20] 歐拉角和萬向節(jié)死鎖