游戏开发中的向量数学

介绍

本教程是线性代数的简短实用介绍,因为它适用于游戏开发。线性代数是向量及其用途的研究。向量在2D和3D开发中都有许多应用,并且Godot广泛使用它们。对矢量数学有深入的了解对于成为一名强大的游戏开发者至关重要。

注意

本教程不是关于线性代数的正式教科书。我们只会研究如何将其应用于游戏开发。要更广泛地了解数学,请参见https://www.khanacademy.org/math/linear-algebra

坐标系(2D)

在2D空间中,使用水平轴(x)和垂直轴(y)定义坐标。2D空间中的特定位置被写为一对值,例如。(4, 3)

../../_images/vector_axis1.png

注意

如果您是计算机图形学的新手,那么正y轴是指向下而不是指向上,这似乎很奇怪,就像您在数学课上学到的那样。但是,这在大多数计算机图形应用程序中很常见。

二维平面中的任何位置都可以通过一对数字来标识。 但是,我们也可以将位置(4,3)视为与(0,0)点或原点的偏移量。 绘制一个从原点指向该点的箭头:

../../_images/vector_xy1.png

这是一个向量。 向量代表许多有用的信息。 除了告诉我们该点位于(4,3)之外,我们还可以将其视为角度θ和长度(或大小)m。 在这种情况下,箭头是位置矢量-它表示相对于原点的空间位置。

关于矢量要考虑的非常重要的一点是,它们仅代表相对方向和大小。没有向量位置的概念。以下两个向量是相同的:

../../_images/vector_xy2.png

两个向量都代表一个点,该点向右4个单位,在某个起点下方3个单位。在平面上绘制矢量的位置无关紧要,它始终表示相对方向和大小。

向量运算

您可以使用任何一种方法(x和y坐标或角度和大小)来引用矢量,但是为了方便起见,程序员通常使用坐标符号。例如,在Godot中,原点是屏幕的左上角,因此,要使用一个名为Node2D400像素,向下300像素的2D节点,请使用以下代码:

var node2D = (Node2D) GetNode("Node2D");
node2D.Position = new Vector2(400, 300);

Godot同时支持Vector2和 Vector3的2D和3D使用。本文讨论的相同数学规则适用于两种类型。

会员访问

可以直接通过名称访问向量的各个组成部分。

// create a vector with coordinates (2, 5)
var a = new Vector2(2, 5);
// create a vector and assign x and y manually
var b = new Vector2();
b.x = 3;
b.y = 1;

添加向量

当相加或相减两个向量时,将添加相应的分量:

var c = a + b;  // (2, 5) + (3, 1) = (5, 6)

我们还可以通过在第一个向量的末尾添加第二个向量来直观地看到这一点:

../../_images/vector_add1.png

注意,加a + b的结果与b + a相同。

标量乘法

注意

向量代表方向和大小。仅代表幅度的值称为标量。

一个向量可以乘以一个标量:

var c = a * 2;  // (2, 5) * 2 = (4, 10)
var d = b / 3;  // (3, 6) / 3 = (1, 2)

../../_images/vector_mult1.png

注意

标量乘以向量不会改变其方向,只会改变其大小。这就是缩放向量的方式。

实际应用

让我们看一下向量加法和减法的两种常见用法。

运动

向量可以表示具有大小和方向的任何数量。典型示例是:位置,速度,加速度和力。在此图像中,步骤1的太空飞船的位置矢量为(1,3),速度矢量为(2,1)。速度矢量表示船每步移动多远。我们可以通过将速度添加到当前位置来找到步骤2的位置。

../../_images/vector_movement1.png

提示

速度测量单位时间的位置变化。通过将速度添加到先前位置来找到新位置。

指向目标

在这种情况下,您有一个坦克,希望将其炮塔指向机器人。从机器人的位置减去水箱的位置即可得出从水箱指向机器人的向量。

../../_images/vector_subtract2.png

提示

要找到一个向量指向A来B使用。B - A

单位向量

大小为的向量1称为单位向量。它们有时也称为方向向量或法线。当需要跟踪方向时,单位矢量会很有用。

正常化

归一化向量意味着将其长度减小到,1同时保留其方向。这是通过将其每个组成部分除以其大小来完成的。因为这是这样一个共同的操作, Vector2并Vector3提供一种用于归一化的方法:

a = a.Normalized();

警告

由于规范化涉及除以向量的长度,因此无法规范化length的向量0。尝试这样做将导致错误。

反射

单位向量的一种常见用法是指示法线。法线向量是垂直于表面对齐并定义其方向的单位向量。它们通常用于照明,碰撞以及涉及曲面的其他操作。

例如,假设我们有一个要从墙或其他物体上反弹的运动球:

../../_images/vector_reflect1.png

表面法线的值为(0,-1),因为它是水平面。 当球碰撞时,我们采取其剩余的运动(当其击中表面时剩余的量)并使用法线反射它。 在Godot中,Vector2类具有bounce()方法来处理此问题。 这是上面使用KinematicBody2D的图的GDScript示例:

// KinematicCollision2D contains information about the collision
KinematicCollision2D collision = MoveAndCollide(_velocity * delta);
if (collision != null)
{
    var reflect = collision.Remainder.Bounce(collision.Normal);
    _velocity = _velocity.Bounce(collision.Normal);
    MoveAndCollide(reflect);
}

点积

该点积是矢量数学最重要的概念之一,但经常被误解。点积是对两个向量返回标量的运算。与既包含幅度又包含方向的向量不同,标量值仅包含幅度。

点积的公式有两种常见形式:

../../_images/vector_dot1.png

../../_images/vector_dot2.png

但是,在大多数情况下,最容易使用内置方法。请注意,两个向量的顺序无关紧要:

float c = a.Dot(b);
float d = b.Dot(a); // These are equivalent.

与单位向量一起使用时,点积最有用,这会使第一个公式简化为just cosθ。这意味着我们可以使用点积来告诉我们有关两个向量之间的角度的一些信息:

../../_images/vector_dot3.png

使用单位矢量时,结果将始终在-1(180°)和1(0°)之间。

面对

我们可以利用这一事实来检测一个对象是否面向另一个对象。在下图中,玩家P试图避开僵尸A和B。假设僵尸的视野为180°,他们可以看到玩家吗?

../../_images/vector_face2.png

绿色箭头fA和fB是代表僵尸面向方向的单位矢量,蓝色半圆形代表其视野。 对于僵尸A,我们使用P-A找到指向玩家的方向向量AP并将其标准化,但是Godot有一个辅助方法来执行此操作,称为direction_to。 如果此向量和面对的向量之间的角度小于90°,则僵尸可以看到玩家。

在代码中,它看起来像这样:

var AP = A.DirectionTo(P);
if (AP.Dot(fA) > 0)
{
    GD.Print("A sees P!");
}

叉积

像点积一样,叉积是对两个向量的运算。但是,叉积的结果是一个向量,向量的方向垂直于两者。其大小取决于它们的相对角度。如果两个向量平行,则其叉积的结果将为空向量。

../../_images/vector_cross1.png
../../_images/vector_cross2.png

叉积计算如下:

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,您可以使用内置方法:

var c = a.Cross(b);

注意

在交叉产品中,订单至关重要。a.cross(b)与给出的结果不同b.cross(a)。所得的矢量指向相反的方向。

计算法线

叉积的一种常见用法是在3D空间中找到平面或曲面的表面法线。如果我们有三角形,ABC则可以使用矢量减法找到两个边AB和AC。使用叉积, 产生一个垂直于两个方向的向量:表面法线。AB x AC

这是一个计算三角形法线的函数:

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中使用向量数学的更多信息,请参见我后续的文章:

进阶向量数学
矩阵与变换

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海拥✘

“听说赞赏的人运气会爆棚哦!”

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值