向量数学
前言
本教程是一个简短而实用的线性代数介绍, 因为它适用于游戏开发. 线性代数是研究向量及其用途的学科. 向量在二维和三维开发中都有许多应用,Godot对它们的应用非常广泛. 要成为一名优秀的游戏开发者, 对向量数学的理解是必不可少的. 注解 不是 线性代数的正式教科书. 我们将只关注它如何应用于游戏开发. 要更全面地了解数学, 请参见 https://www.khanacademy.org/math/linear-algebra
坐标系统(2D)
x) 和垂直轴 (y) 定义坐标. 2D空间中的特定位置被写成一对值, 如 (4, 3).
注解
y 轴的正方向是 向下 , 而不是向上(您在数学课上学到的就像那样). 然而, 这在大多数计算机图形应用程序中是常见的.
(4, 3) 看作是从 (0, 0) 点或 原点 出发的 偏移 . 画一个箭头从原点指向点:
向量 . 向量表示许多有用的信息. 除了告诉我们点在 (4, 3) 之外, 我们还可以把它看成角度 θ 和长度(或幅度) m. 在这种情况下, 箭头是一个 位置向量 -它表示空间中相对于原点的位置.
相对 方向和大小. 没有一个向量的位置的概念. 以下两个向量相同:
这两个向量都表示向右4个单位, 在某个起始点以下3个单位. 不管您在平面上画哪个向量, 它总是代表一个相对的方向和大小.
向量运算
Node2D 的2D节点向右400像素, 向下300像素, 请使用以下代码:
GDScript
C#
$Node2D.position = Vector2(400, 300)
var node2D = GetNode<Node2D>("Node2D");node2D.Position = new Vector2(400, 300);
Vector2 和 Vector3 分别用于2D和3D. 本文讨论的数学规则同样适用于这两种类型.
成员访问
向量的各个组成部分可以直接通过名称访问. GDScript C#
# create a vector with coordinates (2, 5)var a = Vector2(2, 5)# create a vector and assign x and y manuallyvar b = Vector2()b.x = 3b.y = 1
// create a vector with coordinates (2, 5)var a = new Vector2(2, 5);// create a vector and assign x and y manuallyvar b = new Vector2();b.x = 3;b.y = 1;
添加向量
当两个向量相加或相减时, 相应的分量相加: GDScript C#
var c = a + b # (2, 5) + (3, 1) = (5, 6)
var c = a + b; // (2, 5) + (3, 1) = (5, 6)
我们也可以通过在第一个向量的末尾加上第二个向量来直观地看到这一点:
a + b 的加法和 b + a 得到的结果是一样的。
标量乘法
注解 标量 . 一个向量可以乘以一个标量: GDScript C#
var c = a * 2 # (2, 5) * 2 = (4, 10)var d = b / 3 # (3, 6) / 3 = (1, 2)
var c = a * 2; // (2, 5) * 2 = (4, 10)var d = b / 3; // (3, 6) / 3 = (1, 2)
注解 向量乘以标量不会改变它的方向, 只会改变它的幅值. 这就是缩放向量的方法.
实际应用
让我们看看向量加法和减法的两种常见用法.
移动
(1,3) 和一个速度向量为 (2,1). 速度向量表示船舶每一步移动的距离. 通过将速度加到当前位置, 我们可以求出步骤2的位置.
小技巧
速度测量单位时间内位置的变化. 新的位置是通过在前一个位置上增加速度来找到的.
指向一个目标
在这个场景中, 您有一个坦克, 希望它的炮塔指向机器人. 从机器人的位置减去坦克的位置就得到了从坦克指向机器人的向量.
小技巧
A 指向 B 的向量, 请使用 B - A.
单位向量
大小 为 1 的向量称为 单位向量 . 它们有时也被称为 方向向量 或 法线 . 当您需要跟踪一个方向时, 单位向量是有用的.
归一化
归一化 一个向量意味着将其长度缩减到 1 , 并保留其方向. 其方法是将每个分量除以其幅度. 由于这是一个很常见的操作, Vector2 和 Vector3 提供了归一化的方法:
GDScript
C#
a = a.normalized()
a = a.Normalized();
警告
0 的向量进行归一化. 试图这样做会导致错误.
反射
法线 . 法向量是垂直于曲面的单位向量, 定义了曲面的方向. 它们通常用于照明, 碰撞和涉及表面的其他操作.
例如, 假设我们有一个移动的球, 我们想从墙上或其他物体上弹回来:
(0, -1) . 当球碰撞时,我们取它的原运动方向(当它撞到表面时剩余的量), 以法线为角平分线反射它。在Godot中, Vector2 类有一个 bounce() 方法来处理这个问题。这是上图的 GDScript 示例,使用 KinematicBody2D:
GDScript
C#
# object "collision" contains information about the collisionvar collision = move_and_collide(velocity * delta)if collision: var reflect = collision.remainder.bounce(collision.normal) velocity = velocity.bounce(collision.normal) move_and_collide(reflect)
// KinematicCollision2D contains information about the collisionKinematicCollision2D collision = MoveAndCollide(_velocity * delta);if (collision != null){ var reflect = collision.Remainder.Bounce(collision.Normal); _velocity = _velocity.Bounce(collision.Normal); MoveAndCollide(reflect);}
点乘
点乘 是向量数学中最重要的概念之一, 但经常被误解. 点乘是对两个向量的操作, 它返回一个 标量 . 与同时包含大小和方向的向量不同, 标量值只有大小. 点乘公式有两种常见形式: 以及 然而, 在大多数情况下, 使用内置方法是最容易的. 注意, 两个向量的顺序并不重要: GDScript C#
var c = a.dot(b)var d = b.dot(a) # These are equivalent.
float c = a.Dot(b);float d = b.Dot(a); // These are equivalent.
cosθ. 这意味着我们可以使用点积来告诉我们关于两个向量之间的角度的一些信息:
-1 (180°) 和 1 (0°) 之间.
面向问题
P 试图避开丧尸 A 和 B . 假设一个丧尸的视野是 180° , 他们能看到游戏角色吗?
fA 和 fB 是 单位向量 , 代表僵尸的朝向, 蓝色半圆代表其视野. 对于僵尸 A , 我们用 P - A 找到指向玩家的方向向量 AP , 并进行归一化处理, 不过,Godot有一个辅助方法可以做到这一点, 叫做 direction_to . 如果这个向量和面对的向量之间的角度小于90°, 那么僵尸就可以看到玩家.
在代码中是这样的:
GDScript
C#
var AP = A.direction_to(P)if AP.dot(fA) > 0: print("A sees P!")
var AP = A.DirectionTo(P);if (AP.Dot(fA) > 0){ GD.Print("A sees P!");}
叉乘
交叉乘积 也是对两个向量的运算. 但是, 交叉乘积的结果是一个方向与两个向量垂直的向量. 它的大小取决于相对角度. 如果两个向量是平行的, 那么交叉乘积的结果将是一个空向量. 叉乘是这样计算的: GDScript C#
var c = Vector3()c.x = (a.y * b.z) - (a.z * b.y)c.y = (a.z * b.x) - (a.x * b.z)c.z = (a.x * b.y) - (a.y * b.x)
var c = new Vector3();c.x = (a.y * b.z) - (a.z * b.y);c.y = (a.z * b.x) - (a.x * b.z);c.z = (a.x * b.y) - (a.y * b.x);
使用Godot, 您可以使用内置的方法: GDScript C#
var c = a.cross(b)
var c = a.Cross(b);
注解
a.cross(b) 和 b.cross(a) 的结果不一样. 得到的向量指向相反的方向.
法线计算
ABC 我们可以用向量减法找到两条边 AB 和 AC. 通过叉乘, AB x AC 得到一个垂直于这两个向量的向量: 表面法向量.
下面是一个计算三角形法线的函数:
GDScript
C#
func get_triangle_normal(a, b, c): # find the surface normal given 3 vertices var side1 = b - a var side2 = c - a var normal = side1.cross(side2) return normal
Vector3 GetTriangleNormal(Vector3 a, Vector3 b, Vector3 c){ // find the surface normal given 3 vertices var side1 = b - a; var side2 = c - a; var normal = side1.Cross(side2); return normal;}
指向目标
在上面的点积部分, 我们看到如何用它来查找两个向量之间的角度. 然而, 在3D中, 这些信息还不够. 我们还需要知道在围绕什么轴旋转. 我们可以通过计算当前面对的方向和目标方向的交叉乘积来查找. 由此得到的垂直向量就是旋转轴.
更多信息
有关在Godot中使用向量数学的更多信息, 请参阅以下文章:
- 高等向量数学
- 矩阵与变换
