C# Vector3 和 transform.position 篇
〇、前言
系统自带计时器 -System.Diagnostics.StopWatch- 的使用
System.Diagnostics.StopWatch 的基本使用方法
using System.Diagnostics;
Stopwatch timer = new Stopwatch();
timer.Start();
timer.Stop();
decimal second = timer.Elapsed.Ticks * 0.000_000_1m;
decimal microSecond = timer.Elapsed.Ticks * 0.1m;
Debug.Log($"Takes {second:F4} second");
一、三维向量测试
1. 三维向量的函数方法和属性
首先说说 Vector3 的所有函数方法和属性,如下:
----属性名称----
|
--------------------------属性作用------------------------
|
---|
Vector3.right | 返回 new Vector3(1,0,0); ( static ) | Vector3.left | 返回 new Vector3(-1,0,0); ( static ) | Vector3.up | 返回 new Vector3(0,1,0); ( static ) | Vector3.down | 返回 new Vector3(0,-1,0); ( static ) | Vector3.forward | 返回 new Vector3(0,0,1); ( static ) | Vector3.back | 返回 new Vector3(0,0,-1); ( static ) | Vector3.one | 返回 new Vector3(1,1,1); ( static ) | Vector3.zero | 返回 new Vector3(0,0,0); ( static ) | .normalized | 返回该向量的单位向量:
?
e
?
=
n
?
∣
n
?
∣
\ \vec{e} = \frac {\vec{n}} {\vert \vec{n}\vert}
?e
=∣n
∣n
?
| .magnitude | 返回该向量的模:
m
a
g
→
=
∣
n
?
∣
\overrightarrow{mag}= \vert \vec{n}\vert
mag
?=∣n
∣
| .sqrMagnitude | 返回该向量的平方模:
?
s
q
r
M
a
g
=
n
?
?
n
?
=
∣
n
?
∣
2
\ sqrMag = \def\nor{\vec{n}} \nor \cdot \nor = \vert \nor\vert^2
?sqrMag=n
?n
=∣n
∣2;
|
----属性名称----
| 返回值类型 |
-------------------------返回值-----------------------
|
---|
Angle(Vector3 a, Vector3 b) | float | 返回两个向量的夹角(度数):
?
<
a
?
,
b
?
>
=
arccos
?
(
a
?
?
b
?
∣
a
?
∣
?
∣
b
?
∣
)
\ \def\fromVec{\mathsf {\vec a}} \def\toVec{\mathsf {\vec b}} <\vec a,\vec b>= \arccos\left( \frac {\fromVec \cdot \toVec} {\vert\fromVec\vert \cdot \vert \toVec\vert} \right)
?<a
,b
>=arccos(∣a
∣?∣b
∣a
?b
?)
| ClampMagnitude(Vector3 a, float maxLength) | Vector3 | 返回方向与 a 相同,长度最大为 maxLength 的向量:
v
e
c
→
=
min
?
{
a
?
.
m
a
g
n
i
t
u
d
e
,
m
a
x
L
e
n
g
t
h
}
?
a
?
.
n
o
r
m
a
l
i
z
e
\overrightarrow{vec}= \min\{\vec \mathsf a.magnitude,\mathsf {maxLength}\}\cdot \vec \mathsf a.normalize
vec
=min{a
.magnitude,maxLength}?a
.normalize
| Cross(Vector3 a, Vector3 b) | Vector3 | 返回两个向量的叉积:
?
v
e
c
→
=
a
?
×
b
?
=
∣
i
?
j
?
k
?
a
?
.
x
a
?
.
y
a
?
.
z
b
?
.
x
b
?
.
y
b
?
.
z
∣
\ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} \overrightarrow{vec} = \A \times \B = \begin{vmatrix} \vec i & \vec j & \vec k \\ \A .x & \A.y & \A.z \\ \B.x & \B.y & \B.z\end{vmatrix}
?vec
=a
×b
=∣∣∣∣∣∣?i
a
.xb
.x?j
?a
.yb
.y?k
a
.zb
.z?∣∣∣∣∣∣?
| Dot(Vector3 a, Vector3 b) | float | 返回两个向量的点积:
?
r
e
s
u
l
t
=
a
?
?
b
?
=
a
?
.
x
?
b
?
.
x
+
a
?
.
y
?
b
?
.
y
+
a
?
.
z
?
b
?
.
z
\ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} result= \A \cdot \B = \A.x*\B.x+\A.y*\B.y+\A.z*\B.z
?result=a
?b
=a
.x?b
.x+a
.y?b
.y+a
.z?b
.z
| Scale(Vector3 a, Vector3 b) | Vector3 | 返回两个向量各部分对应相乘的结果:
v
e
c
→
=
n
e
w
?
V
e
c
t
o
r
3
(
a
?
.
x
?
b
?
.
x
,
a
?
.
y
?
b
?
.
y
,
a
?
.
z
?
b
?
.
z
)
;
\def\A{\vec\mathsf a}\def\B{\vec\mathsf b} \overrightarrow\mathsf {vec} = \mathsf {new \ Vector3}( \A.x*\B.x, \A.y*\B.y, \A.z*\B.z);
vec
=new?Vector3(a
.x?b
.x,a
.y?b
.y,a
.z?b
.z);
| Distance(Vector3 a, Vector3 b) | float | 返回a到b的距离:
?
d
i
s
=
∣
b
?
?
a
?
∣
\ dis = \vert \vec\mathsf b - \vec\mathsf a\vert
?dis=∣b
?a
∣
| Lerp(Vector3 a, Vector3 b, float t) | Vector3 | 返回a到b的插值( 其中t 会被限制在0~1之间 ):
v
e
c
→
=
a
?
+
(
b
?
?
a
?
)
?
t
\overrightarrow{vec}= \vec\mathsf a + (\vec\mathsf b - \vec\mathsf a)*\mathsf t
vec
=a
+(b
?a
)?t
| LerpUnclamp(Vector3 a, Vector3 b, float t) | Vector3 | 返回a到b的插值( t 不会被限制在0~1之间 ):
v
e
c
→
=
a
?
+
(
b
?
?
a
?
)
?
t
\overrightarrow{vec}= \vec\mathsf a + (\vec\mathsf b - \vec\mathsf a)*\mathsf t
vec
=a
+(b
?a
)?t
| Magnitude(Vector3 vector) | float | 返回 vector 的模:
?
m
a
g
=
∣
v
e
c
t
o
r
→
∣
\ mag = \vert\overrightarrow\mathsf {vector} \vert
?mag=∣vector
∣
| Normalize(Vector3 vector) | Vector3 | 返回 vector 的单位向量:
?
e
?
=
v
e
c
t
o
r
→
∣
v
e
c
t
o
r
→
∣
\ \vec e = \frac {\overrightarrow\mathsf {vector}} {\vert\overrightarrow\mathsf {vector} \vert}
?e
=∣vector
∣vector
?
| SqrMagnitude(Vector3 vector) | float | 返回 vector 的平方模:
s
q
r
M
a
g
=
v
e
c
t
o
r
→
?
v
e
c
t
o
r
→
\def\Vec{\overrightarrow\mathsf {vector}} sqrMag = \Vec \cdot \Vec
sqrMag=vector
?vector
;
| Max(Vector3 a, Vector3 b) | Vector3 | 返回由两个向量最大值部分组成的向量:
?
v
e
c
→
=
n
e
w
?
V
e
c
t
o
r
3
(
max
?
{
a
?
.
x
,
b
?
.
x
}
,
max
?
{
a
?
.
y
,
b
?
.
y
}
,
max
?
{
a
?
.
z
,
b
?
.
z
}
)
;
\ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} \overrightarrow{vec}= \mathsf {new \ Vector3}(\max\{\A.x,\B.x\},\max\{\A.y,\B.y\},\max\{\A.z,\B.z\});
?vec
=new?Vector3(max{a
.x,b
.x},max{a
.y,b
.y},max{a
.z,b
.z});
| Min(Vector3 a, Vector3 b) | Vector3 | 返回由两个向量最小值部分组成的向量:
?
v
e
c
→
=
n
e
w
?
V
e
c
t
o
r
3
(
min
?
{
a
?
.
x
,
b
?
.
x
}
,
min
?
{
a
?
.
y
,
b
?
.
y
}
,
min
?
{
a
?
.
z
,
b
?
.
z
}
)
;
\ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} \overrightarrow {vec}= \mathsf {new \ Vector3}(\min\{\A.x,\B.x\},\min\{\A.y,\B.y\},\min\{\A.z,\B.z\});
?vec
=new?Vector3(min{a
.x,b
.x},min{a
.y,b
.y},min{a
.z,b
.z});
| MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta) | Vector3 | 返回从 current 沿 “ current 指向 target ” 方向移动 maxDistanceDelta 距离后的向量(令 direction = (target - current).Normalized();):
v
e
c
→
=
c
u
r
r
e
n
t
→
+
d
i
r
e
c
t
i
o
n
→
?
m
a
x
D
i
s
t
a
n
c
e
D
e
l
t
a
\overrightarrow{vec}= \overrightarrow\mathsf {current}+ \overrightarrow\mathsf {direction}*\mathsf {maxDistanceDelta}
vec
=current
+direction
?maxDistanceDelta
| Project(Vector3 vector, Vector3 onNormal) | Vector3 | 返回 vector 在 onNormal 上的投影(令 normal = onNormal.Normalized();):
v
e
c
→
=
(
v
e
c
t
o
r
→
?
n
o
r
m
a
l
→
)
?
n
o
r
m
a
l
→
\overrightarrow{vec} = \def\Nor{\overrightarrow\mathsf {normal}} \left(\overrightarrow\mathsf {vector}\cdot\Nor \right)\cdot \Nor
vec
=(vector
?normal
)?normal
| ProjectOnPlane(Vector3 vector, Vector3 planeNormal) | Vector3 | 返回 vector 在面上的投影(令 normal = planeNormal.Normalized();):
v
e
c
→
=
v
e
c
t
o
r
→
?
(
v
e
c
t
o
r
→
?
n
o
r
m
a
l
→
)
?
n
o
r
m
a
l
→
\overrightarrow{vec} = \def\Vec{\overrightarrow\mathsf {vector}} \def\Nor{\overrightarrow\mathsf {normal}} \Vec - \left(\Vec \cdot\Nor\right)\cdot \Nor
vec
=vector
?(vector
?normal
)?normal
| Reflect(Vector3 inDirection, Vector3 inNormal) | Vector3 | 返回 inDirection 在 inNormal 上的反射(令 normal = inNormal.Normalized(); dir = inDirection;):
v
e
c
→
=
d
i
r
→
?
2
?
(
d
i
r
→
?
n
o
r
m
a
l
→
)
?
n
o
r
m
a
l
→
\overrightarrow\mathsf {vec} = \def\Dir{\overrightarrow\mathsf{dir}} \def\Nor{\overrightarrow\mathsf{normal}} \Dir - 2 *\left(\Dir \cdot \Nor\right) \cdot \Nor
vec
=dir
?2?(dir
?normal
)?normal
| SignedAngle(Vector3 from, Vector3 to, Vector3 axis) | float | 返回两个向量在法向量为 axis 的面上的投影之间的夹角:
∠
a
n
g
l
e
=
A
n
g
l
e
(
P
r
o
j
e
c
t
O
n
P
l
a
n
e
(
f
r
o
m
?
,
a
x
i
s
?
)
,
P
r
o
j
e
c
t
O
n
P
l
a
n
e
(
t
o
?
,
a
x
i
s
?
)
)
;
\angle angle = \mathsf{Angle(ProjectOnPlane(\vec {\bold {from}},\vec {\bold {axis}}),ProjectOnPlane(\vec {\bold {to}},\vec {\bold {axis}}))};
∠angle=Angle(ProjectOnPlane(from
,axis
),ProjectOnPlane(to
,axis
));
|
----属性名称----
| 返回值类型 |
-------------------------返回值-----------------------
|
---|
.Scale(Vector3 scale) | void | 该向量为与 scale 各部分对应相乘的结果:
v
e
c
→
=
n
e
w
?
V
e
c
t
o
r
3
(
x
?
s
a
c
l
e
→
.
x
,
y
?
s
a
c
l
e
→
.
y
,
z
?
s
a
c
l
e
→
.
z
)
;
\def\B{\overrightarrow\mathsf {sacle}} \overrightarrow\mathsf {vec} = \mathsf{new \ Vector3}( x*\B.x, y*\B.y, z*\B.z);
vec
=new?Vector3(x?sacle
.x,y?sacle
.y,z?sacle
.z);
| .Set(float newX, float newY, float newZ) | void | 不开新的内存空间的改变该向量值的方法:
v
e
c
→
=
n
e
w
?
V
e
c
t
o
r
3
(
n
e
w
X
,
n
e
w
Y
,
n
e
w
Z
)
;
\overrightarrow\mathsf {vec} = \mathsf{new \ Vector3}(\mathsf{newX}, \mathsf{newY}, \mathsf{newZ});
vec
=new?Vector3(newX,newY,newZ);
| .Normalize() | void | 单位化该向量:
e
?
=
n
?
n
?
.
m
a
g
n
i
t
u
d
e
\vec e = \frac{\vec n}{\vec n.\mathsf magnitude}
e
=n
.magnituden
?
|
2. 三维向量测试
介绍完三维向量的基本常用函数方法,接下来将进行一系列测试来说明各方法的最佳使用环境。 接下来所有测试都以以下代码为标准代码块(跟详细的见测试代码运行所用时间(一)),并在测试不同内容时进行不同修改:
...
private int count = 100_000;
...
private void OnClicked()
{
...
for (int testIndex = -1; testIndex < (n + 1) * 3; ++testIndex)
{
...
int index = Mathf.FloorToInt(testIndex * 0.334f);
Stopwatch timer = new Stopwatch();
timer.Start();
switch (index)
{
case -1:
break;
case 0:
for (int i = 0; i < count; ++i) { }
break;
case 1:
...
break;
...
}
timer.Stop();
UnityEngine.Debug.Log($"{index}: Takes -{timer.Elapsed.Ticks}- ticks");
...
}
}
(1). 初始化、创建和赋值
Vector3 dir = Vector3.zero;
其中 测试实验个数 n = 4
case 1:
for (int i = 0; i < count; ++i)
{
Vector3 tmpDir = Vector3.up;
}
break;
case 2:
for (int i = 0; i < count; ++i)
{
dir = Vector3.up;
}
break;
case 3:
for (int i = 0; i < count; ++i)
{
dir = new Vector3(0, 1, 0);
}
case 4:
for (int i = 0; i < count; ++i)
{
dir.Set(0, 1, 0);
}
break;
在Unity中多次测试后取稳定值,记录实验数据:
次数\实验编号 | 0 | 1 | 2 | 3 | 4 |
---|
1 | 608 | 912 | 1824 | 5511 | 5477 | 2 | 609 | 912 | 1823 | 5488 | 5488 | 3 | 612 | 913 | 1824 | 5468 | 5511 |
(2). Distance、magnitude 和 sqrMagnitude
Vector3 targetPos = Random.onUnitSphere * Random.Range(0f, 42f);
Vector3 startPos = Vector3.zero;
float len = 0;
其中 测试实验个数 n = 5
case 1:
for (int i = 0; i < count; ++i)
{
len = Vector3.Distance(startPos,targetPos);
}
break;
case 2:
for (int i = 0; i < count; ++i)
{
len = targetPos.magnitude;
}
break;
case 3:
for (int i = 0; i < count; ++i)
{
len = Vector3.Magnitude(targetPos);
}
case 4:
for (int i = 0; i < count; ++i)
{
len = targetPos.sqrMagnitude;
}
break;
case 5:
for (int i = 0; i < count; ++i)
{
len = Vector3.SqrMagnitude(targetPos);
}
break;
在Unity中多次测试后取稳定值,记录实验数据:
次数\实验编号 | 0 | 1 | 2 | 3 | 4 | 5 |
---|
1 | 629 | 66122 | 12316 | 11762 | 7518 | 8677 | 2 | 555 | 65797 | 23677 | 20418 | 13609 | 11996 | 3 | 503 | 65150 | 22043 | 15769 | 7084 | 7656 | 4 | 752 | 63884 | 12015 | 13409 | 11712 | 8624 | 5 | 630 | 62636 | 12397 | 16274 | 7156 | 7714 |
由上述实验数据可得:
在进行向量之间距离的比较时,最好用 sqrMagnitude; 如果需要用到向量的模就还是用 magnitude;
关于 Distance 和 magnitude 的区别,本博主认为是两个向量差引起的,所以再次进行实验:
Vector3 targetPos = Random.onUnitSphere * Random.Range(0f, 42f);
Vector3 startPos = Vector3.zero;
Vector3 dir = targetPos - startPos;
float len = 0;
其中 测试实验个数 n = 4
case 1:
for (int i = 0; i < count; ++i)
{
len = Vector3.Distance(startPos,targetPos);
}
break;
case 2:
for (int i = 0; i < count; ++i)
{
len = (targetPos - startPos).magnitude;
}
break;
case 3:
for (int i = 0; i < count; ++i)
{
len = Vector3.Magnitude(targetPos - startPos);
}
case 4:
for (int i = 0; i < count; ++i)
{
len = dir.magnitude;
}
break;
再在Unity中进行多次测试后取稳定值,记录实验数据:
次数\实验编号 | 0 | 1 | 2 | 3 | 4 |
---|
1 | 837 | 38458 | 40470 | 46247 | 10289 | 2 | 1044 | 29661 | 40310 | 44343 | 9605 | 3 | 804 | 28686 | 39578 | 44613 | 9601 |
由此可发现:
在 distance 和 magnitude 中: 如果要计算两点之间的距离,用 distance 是最好的选择; 如果要计算一个向量的模,用 magnitude 是最好的选择;
(3). 由 normalize 引出的各个数字运算耗费时间
一般情况下,我们都会认为 单位化向量 的性能消耗比较大,以为其中有
1
n
?
?
n
?
\frac {1} {\sqrt{\vec n \cdot \vec n}}
n
?n
?1?,既有除的操作,又有根号的操作。 于是下面先来测试一下各个运算符的性能消耗:
float creator = 1.0001f;
float sqrtNum = 114514.114514f;
double outsideNum = 0;
double num = 1314.520f;
其中 测试实验个数 n = 8
case 1:
for (int i = 0; i < count; ++i)
{
num += creator;
}
break;
case 2:
for (int i = 0; i < count; ++i)
{
num -= creator;
}
break;
case 3:
for (int i = 0; i < count; ++i)
{
num *= creator;
}
case 4:
for (int i = 0; i < count; ++i)
{
num /= creator;
}
break;
case 5:
for (int i = 0; i < count; ++i)
{
num %= creator;
}
break;
case 6:
for (int i = 0; i < count; ++i)
{
num = sqrtNum;
}
break;
case 7:
for (int i = 0; i < count; ++i)
{
num = Mathf.Sqrt(sqrtNum);
}
case 8:
for (int i = 0; i < count; ++i)
{
num = System.Math.Sqrt(sqrtNum);
}
break;
outsideNum = num;
在Unity中进行多次测试后取稳定值,记录实验数据:
次数\实验编号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|
1 | 837 | 3827 | 3776 | 3761 | 8161 | 14868 | 3175 | 28922 | 2418 | 2 | 879 | 3760 | 3761 | 3760 | 8492 | 14777 | 2877 | 29522 | 2517 | 3 | 838 | 3950 | 3789 | 3760 | 8168 | 14890 | 3742 | 29861 | 2673 |
本博主为了方便观察,画出直观图:
:05
:10
:15
:20
:25
:30
3760~3873
3759~3903
3873~4258
3760~3958
3760~3778
3782~5465
3758~3829
3760~3848
3759~3773
8160~8166
8158~8697
8151~8341
14766~15025
14774~14949
14856~15055
2744~3577
2868~4044
3369~4163
28363~29649
29500~29701
29555~31073
2409~2420
2511~2526
2520~4093
加
减
乘
除
求余
(double)float
Unity Sqrt
System Sqrt
数字运算性能测试直观图
但是 System.Math.Sqrt 的超短时间计算确实出乎意料。本博主测试了每次测试 count *= 10 ,结果发现 System.Math.Sqrt 与 count 成线性相关 还是 1:1 那种。而且每次计算结果和 Unity.Mathf.Sqrt 的计算结果一样。
这次意料之外的测试在以后再进行探究。
接下来测试一下 Normalize 和 Sqrt 的时间消耗:
Vector3 targetPos = Random.onUnitSphere * Random.Range(0f, 42f);
Vector3 norDir;
double outsideNum;
double num = 1314.520f;
其中 测试实验个数 n = 7
case 1:
for (int i = 0; i < count; ++i)
{
norDir = dir.normalized;
}
break;
case 2:
for (int i = 0; i < count; ++i)
{
dir.Normalize();
norDir = dir;
}
break;
case 3:
for (int i = 0; i < count; ++i)
{
Vector3.Normalize(dir);
norDir = dir;
}
case 4:
for (int i = 0; i < count; ++i)
{
outsideNum = Mathf.Sqrt((float)num);
}
break;
case 5:
for (int i = 0; i < count; ++i)
{
float sqrMag = dir.sqrMagnitude;
norDir = dir / Mathf.Sqrt(sqrMag);
}
break;
case 6:
for (int i = 0; i < count; ++i)
{
float sqrMag = dir.x * dir.x + dir.y * dir.y + dir.z * dir.z;
norDir = dir / Mathf.Sqrt(sqrMag);
}
break;
case 7:
for (int i = 0; i < count; ++i)
{
float sqrMag = dir.x * dir.x + dir.y * dir.y + dir.z * dir.z;
norDir = dir / System.Math.Sqrt(sqrMag);
}
break;
norDir = Vector3.zero;
在Unity中多次测试后取稳定值,记录实验数据:
次数\实验编号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|
1 | 504 | 110790 | 89339 | 102991 | 36709 | 82813 | 70295 | 50343 | 2 | 602 | 103803 | 84620 | 97766 | 32458 | 80954 | 69105 | 49984 | 3 | 486 | 104342 | 84593 | 97936 | 34093 | 84698 | 69129 | 47001 |
由实验数据可知 Normalize 的性能与 直接 1/Mathf.Sqrt(.sqrMagnitude) 没有太大的区别。而且使用 1/System.Math.Sqrt(.sqrMagnitude) 会比正常1/Mathf.Sqrt(.sqrMagnitude) 快一些,速度比大概是10:7 ; 还比 Normalize 快一倍多。
二、变换系统测试
1. transform 的结构
transform 中的所有属性如下:  transform 中的所有方法如下:
2. transform.position 赋值和获取
Vector3 startPos = transform.position;
Vector3 assignPos;
其中 测试实验个数 n = 4
case 1:
for (int i = 0; i < count; ++i)
{
assignPos = transform.position;
}
break;
case 2:
for (int i = 0; i < count; ++i)
{
transform.position += Vector3.up;
}
break;
case 3:
for (int i = 0; i < count; ++i)
{
transform.position = Vector3.up;
}
break;
case 4:
for (int i = 0; i < count; ++i)
{
assignPos = Vector3.up;
}
break;
transform.position = startPos;
再在Unity中进行多次测试后取稳定值,记录实验数据:
次数\实验名称 | 对比组 | 获取 | 加赋值 | 赋值 | 向量赋值 |
---|
1 | 288 | 118292 | 192474 | 117565 | 1949 | 2 | 381 | 158737 | 267344 | 158888 | 2264 | 3 | 515 | 171582 | 285283 | 153551 | 2276 |
由实验记录可知,单单获取 transform.position 的时间消耗和赋值 transform.position 差不多,甚至还是给普通向量赋值的 70 倍。 可以得出结论:
如果要使一个物体移动,先计算好所有向量的合向量,再给 transform.position 赋值,也不能频繁获取 transform.position ,如果常用的话就先储存在 Vector3 中。
新人博主,请大家多多光照~~如果有什么不正确或不足的地方请在评论区积极指出哟,一起学习一起进步~
|