# 使用矢量移动对象

> Perform vector arithmetic with Unity's 2D, 3D, and 4D vector classes to manage the position and speed of objects and the distance between them.

矢量是一个基本的数学概念，用于描述方向和大小。在游戏和应用程序中，矢量通常用于描述基本属性，例如角色的位置、物体移动的速度或两个对象之间的距离。

矢量算术是计算机编程很多方面（如图形、物理和动画）的基础，深入了解这一主题对于充分发挥 Unity 的功能很有帮助。

矢量可以在多个维度上表示，Unity 提供 Vector2、Vector3 和 Vector4 类来处理 2D、3D 和 4D 矢量。这三个类型的矢量类都共享许多相同的功能，例如大小，因此除非另有说明，否则本页上的大部分信息都适用于所有三个类型的矢量。

本页面概述矢量类及其使用这些类编写脚本时的常见用法。有关矢量类每个成员的详尽参考，请参阅脚本参考页面中的 [Vector2](/engine/6000.0/script-reference/unity-engine/vector-2.md)、[Vector3](/engine/6000.0/script-reference/unity-engine/vector-3.md) 和 [Vector4](/engine/6000.0/script-reference/unity-engine/vector-4.md)。

## 了解矢量算术

### 添加

当两个矢量相加时，结果相当于将原始矢量依次作为“步骤”。请注意，两个参数的顺序无关紧要，因为两种方式的结果都相同。


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/VectorAdd.png)

如果将第一个矢量视为空间中的一个点，那么第二个矢量可以解释为从该位置的偏移或“跳跃”。例如，为了找到地面上某个位置上方 5 个单位的点，可使用以下计算：

```
 var pointInAir = pointOnGround + new Vector2(0, 5);
```

如果矢量代表力，那么从力的方向和大小来考虑它们将会更直观。两个力矢量相加会产生一个等于力的组合的新矢量。施加的力同时有若干单独分量起作用时（例如，向前推进的火箭还可能受到侧风影响），此概念通常很有用。

此处的示例使用的是 2D 矢量，但相同的概念也适用于 3D 和 4D 矢量。

### 减法

矢量减法通常用于获取从一个对象到另一个对象的方向和距离。请注意，两个参数的顺序对于减法很\_\_重要\_\_：


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/VectorSubtract.png)

```
// The vector d has the same magnitude as c but points in the opposite direction.
var c = b - a;
var d = a - b;
```

与数字一样，与负向矢量相加相当于减去正向矢量。

```
// These both give the same result.
var c = a - b;
var c = a + -b;
```

负向矢量的大小与原始矢量相同并沿着同一条线指向，但在完全相反的方向上。

## 从一个对象到另一个对象的方向和距离

如果从空间中的一个点减去另一个点，则得到的结果是从一个对象“指向”另一个对象的矢量：

```
// Gets a vector that points from the player's position to the target's.
var heading = target.position - player.position;
```

除了指向目标对象的方向之外，该矢量的大小等于两个位置之间的距离。您需要“归一化”矢量来提供目标的方向，但距离固定（例如用于指挥飞弹）。可以将矢量除以其大小，从而将其归一化：

```
var distance = heading.magnitude;
var direction = heading / distance; // This is now the normalized direction.
```

此方法优于单独使用大小和归一化属性，因为大小和归一化属性都非常耗费 CPU（都涉及计算平方根）。

如果只需要使用距离进行比较（例如，进行接近检查），则可以完全避免大小计算。`sqrMagnitude` 属性给出大小值的平方，计算方式与大小相似，但不需要进行耗时的平方根运算。不要将大小与已知距离进行比较，可将大小的平方与距离的平方进行比较：

```
if (heading.sqrMagnitude < maxRange * maxRange) {
	// Target is within range.
}
```

这种算法比在比较中使用真实大小要高效得多。

有时，在 3D 中工作时，可能需要一个到目标的“平行于地面的方向”。例如，想象一个站在地面上的玩家需要接近漂浮在空中的目标。如果从目标位置减去玩家位置，那么产生的矢量将向上指向目标。这种情况下不适合对玩家变换进行定向，因为变换也会指向上方；真正需要的是从玩家位置到目标正下方地面位置的矢量。通过使用减法结果并将 Y 坐标设置为零，可以获得该矢量：

```
var heading = target.position - player.position;
heading.y = 0;	// This is the overground heading.
```

## 标量乘法和除法

在讨论矢量时，通常将普通数（例如，浮点值）称为标量。这意味着标量只有“标度”或大小，而矢量兼具大小和方向。

将矢量乘以标量会产生与原始矢量方向相同的矢量。但是，新矢量的大小等于原始大小乘以标量值。

同样，标量除法将原始矢量的大小除以标量。

当矢量表示移动偏移或力时，这些运算很有用。通过这些运算可以更改矢量的大小而不影响其方向。

当任何矢量除以其自身的大小时，得到的结果是大小为 1 的矢量，即所谓的归一化矢量。如果归一化矢量乘以标量，则结果的大小将等于该标量值。当力的方向恒定但强度可控时（例如，来自车轮的力总是向前推动，但是动力由驾驶员控制），这会很有用。

## 点积

点积取两个矢量并返回标量。该标量等于两个矢量相乘的大小，得到的结果再乘以矢量之间角度的余弦。当两个矢量都被归一化时，余弦本质上表示第一个矢量在第二个矢量的方向上延伸的距离（反之亦然 - 参数的顺序无关紧要）。


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/DotProduct.png)

下面对与参考矢量相比不同角度的矢量如何返回介于 1 和 -1 之间的点积值进行对比：


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/CosineValues.png)

点积是一种比余弦更简单的数学运算，因此在某些情况下可用于代替 `Mathf.Cos` 函数或矢量大小运算（功能不完全相同但有时效果相同）。但是，计算点积函数所需的 CPU 时间要少得多，因此可作为一种有价值的优化。

如果需要计算一个矢量在另一个矢量方向上的大小，点积很有用。

例如，汽车的速度计一般用于测量车轮的转速。汽车可能不会直接向前移动（例如，可能侧向打滑），在此情况下，部分运动不是朝向汽车前方，因此速度计无法测量。对象的 `rigidbody.velocity` 矢量的大小将给出整体运动方向上的速度，但是为了单独考虑向前的速度，应使用点积：

```
var fwdSpeed = Vector3.Dot(rigidbody.velocity, transform.forward);
```

方向可以是您喜欢的任何方向，但为了进行此计算，必须对方向矢量进行归一化。这样，不仅结果比速度大小更准确，而且无需执行在查找大小时需要的缓慢的平方根运算。

## 叉积

叉积仅对 3D 矢量有意义。它需要两个 3D 矢量作为输入，并返回另一个 3D 矢量作为结果。

结果矢量垂直于两个输入向量。可使用“右手螺旋规则”根据输入矢量的排序确定输出矢量的方向。如果按照输入矢量的顺序弯曲手指，那么大拇指的指向就是输出矢量的方向。如果参数的顺序颠倒，得到的矢量将指向完全相反的方向，但大小相同。

结果的大小等于输入矢量的大小相乘，然后该值再乘以二者之间角度的正弦。正弦函数的一些有用值如下所示：


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/SineValues.png)

差积看起来很复杂，因为它在返回值中结合了多方面的有用信息。然而，就像点积一样，它在数学上的效率非常高，可用于优化代码，否则这些代码将不得不依赖于更缓慢的超越函数，如正弦和余弦函数。

## 计算法向/垂直矢量

在网格生成期间经常需要“法向”矢量（即，垂直于平面的矢量），此外，法向矢量还用于路径跟踪和其他情况。在平面中给定三个点（比如网格三角形的角点），就能够找到法线，如下所示：

* 选择三个点之一
* 分别从其他两个点中的每一个中减去它（产生两个新矢量，“Side 1”和“Side 2”）
* 计算矢量“Side 1”和“Side 2”的叉积
* 叉积的结果是一个新的矢量，它垂直于三个原始点所在的平面，也就是“法线”。


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/CalculateNormal.png)

```
Vector3 a;
Vector3 b;
Vector3 c;

Vector3 side1 = b - a;
Vector3 side2 = c - a;

Vector3 normal = Vector3.Cross(side1, side2);
```


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/LeftHandRuleDiagram.png)

使用“左手规则”可确定将这两个矢量传递到叉积函数的顺序。在表面上方从上往下看（法线将指向外部）时，第一个矢量应顺时针扫过第二个矢量：

如果输入矢量的顺序颠倒，结果将指向完全相反的方向。

对于网格，法向矢量也必须归一化。可通过归一化属性来实现此目的，但是还有另一个偶尔有用的技巧。还可以将垂直矢量除以其大小，从而将其归一化：

```
float perpLength = perp.magnitude;
perp /= perpLength;
```

另外需要注意的是，三角形的面积等于 `perpLength` / 2。如果需要得出整个网格的表面积，或者想要根据相对面积随机选择三角形，这会很有用。

## 其他资源

* [Vector2 API 参考](/engine/6000.0/script-reference/unity-engine/vector-2.md)
* [Vector3 API 参考](/engine/6000.0/script-reference/unity-engine/vector-3.md)
* [Vector4 API 参考](/engine/6000.0/script-reference/unity-engine/vector-4.md)
