Version: 2020.3
重要的类 - Transform
重要的类 - Quaternion

重要的类 - 矢量

Vectors are a fundamental mathematical concept which allow you to describe a direction and magnitude. In games and apps, vectors are often used to describe some of the fundamental properties such as the position of a character, the speed something is moving, or the distance between two objects.

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

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

本页面概述矢量类及其使用这些类编写脚本时的常见用法。有关矢量类每个成员的详尽参考,请参阅脚本参考页面中的 Vector2Vector3Vector4

了解矢量算术

加法

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

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

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

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

尽管此处的示例显示的是 2D 矢量,但相同的概念也适用于 3D 和 4D 矢量。

减法

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

// 矢量 d 的大小与 c 相同,但指向相反的方向。
var c = b - a;
var d = a - b;

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

// 这两者得出相同的结果。
var c = a - b;
var c = a + -b;

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

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

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

// 获取从玩家位置指向目标位置的矢量。
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) {
    // 目标在范围内。
}

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

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

var heading = target.position - player.position;
heading.y = 0;  // 这是平行于地面的方向。

标量乘法和除法

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

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

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

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

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

点积

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

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

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

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

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

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

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

叉积

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

The result vector is perpendicular to the two input vectors. You can use the “right hand screw rule” to remember the direction of the output vector from the ordering of the input vectors. If you can curl your fingers in the order of the input vectors, your thumb points in the direction of the output vector. If the order of the parameters is reversed then the resulting vector will point in the exact opposite direction but will have the same magnitude.

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

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

计算法向/垂直矢量

在网格生成期间经常需要“法向”矢量(即,垂直于平面的矢量),此外,法向矢量也用于路径跟踪和其他情况。在平面中给定三个点(比如网格三角形的角点),就很容易找到法线,如下所示: - 选择三个点之一 - 分别从其他两个点中的每一个中减去它(产生两个新矢量,“Side 1”和“Side 2”) - 计算矢量“Side 1”和“Side 2”的叉积 - 叉积的结果是一个新的矢量,它垂直于三个原始点所在的平面,也就是“法线”。

Vector3 a;
Vector3 b;
Vector3 c;

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

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

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

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

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

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

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

重要的类 - Transform
重要的类 - Quaternion