矩阵和变换

Godot Matrices and transforms(矩阵和变换)

介绍

在阅读本教程之前,我们建议您彻底阅读并理解矢量数学教程,因为本教程要求您具备矢量知识。

本教程是关于变换以及我们如何在Godot中使用矩阵来表示它们。它不是一个完整深入的矩阵指南。变换大多数时候是以平移、旋转和缩放的方式应用的,所以我们将重点介绍如何用矩阵来表示这些变换。

本指南的大部分内容集中在2D,使用Transform2D和Vector2,但在3D中的工作方式非常相似。
强调文本 强调文本

正如在前面的教程中所提到的,重要的是要记住,在Godot中,Y轴在二维中是指向下方的。这与大多数学校教授线性代数的方式相反,Y轴指向上。

惯例是 X 轴为红色,Y 轴为绿色,Z 轴为蓝色。本教程的颜色编码与这些约定相符,但我们也会用蓝色来表示原点向量。

矩阵分量和单位矩阵

单位矩阵表示一个没有平移、没有旋转、没有缩放的变换。让我们先看看单位矩阵以及它的分量是如何与它的外观相联系的
在这里插入图片描述
矩阵有行和列,而变换矩阵对于每个矩阵的作用有特定的约定。

在上图中,我们可以看到红色的X向量由矩阵的第一列表示,绿色的Y向量同样由第二列表示。 更改列将更改这些向量。 在接下来的几个示例中,我们将看到如何对其进行操作。

您不应该担心直接操作行,因为我们通常使用列。但是,你可以把矩阵的行看作是表示哪些向量有助于在给定的方向上移动。

当我们提到t.x.y这样的值时,那就是X列向量的Y分量。换句话说,就是矩阵的左下角。同理,t.x.x是左上角,t.y.x是右上角,t.y.y是右下角,其中t是Transform2D。
在这里插入图片描述
现在,为了缩放矩阵,我们所要做的就是把每个分量乘以我们想要的缩放。把它放大2。1乘以2等于2,0乘以2等于0,所以我们得到这个
在这里插入图片描述
为了在代码中做到这一点,我们可以简单地乘以每个向量:

var t = Transform2D()
# Scale
t.x *= 2
t.y *= 2
transform = t # Change the node's transform to what we just calculated.

如果我们想把它恢复到原来的比例,我们可以把每个分量乘以0.5。这几乎就是缩放变换矩阵的全部内容。
要从现有的变换矩阵计算对象的比例,可以在每个列向量上使用length()。

在实际项目中,你可以使用scaled()方法来执行缩放。

旋转变换矩阵

我们将以与前面相同的方式开始,在单位矩阵下面使用Godot标识:
在这里插入图片描述
例如,我们想要顺时针旋转Godot标志90度。现在X轴向右,Y轴向下。如果我们在脑海中旋转这些,我们会逻辑地看到新的X轴应该指向下面,新的Y轴应该指向左边。

可以想象,同时抓取Godot标识及其矢量,然后围绕中心旋转它。无论你在哪里结束旋转,向量的方向决定了矩阵是什么。

我们需要在法线坐标中表示“下”和“左”,所以这意味着我们将X设置为(0,1),将Y设置为(-1,0)。 这些也是Vector2.DOWN和Vector2.LEFT的值。 当我们这样做时,我们得到旋转对象的预期结果:
在这里插入图片描述
如果你理解上面的问题有困难,试试这个练习:剪一张正方形的纸,在上面画出X和Y向量,把它放在图形纸上,然后旋转它并注意端点。

要在代码中执行旋转,我们需要能够以编程的方式计算值。该图像显示了从旋转角度计算变换矩阵所需的公式。如果这部分看起来很复杂,请不要担心,我保证这是您需要知道的最困难的事情。
在这里插入图片描述
在这里插入图片描述

戈多用弧度表示所有的旋转,而不是角度。一个完整的旋转是TAU 或 PI*2,90度的四分之一旋转是 TAU/4 o或PI/2 。使用TAU通常会产生更易读的代码。

有趣的是:除了在Godot中Y是向下的,旋转是顺时针方向的。这意味着所有的数学和三角函数都与Y向上的CCW系统表现相同,因为这些差异 "抵消 "了。你可以认为两个系统中的旋转都是 “从X到Y”。

为了执行一个0.5弧度(约28.65度)的旋转,我们只需在上面的公式中插入一个0.5的值,然后计算出实际值应该是多少:
在这里插入图片描述
下面是如何在代码中完成(将脚本放在一个Node2D):

var rot = 0.5 # The rotation to apply.
var t = Transform2D()
t.x.x = cos(rot)
t.y.y = cos(rot)
t.x.y = sin(rot)
t.y.x = -sin(rot)
transform = t # Change the node's transform to what we just calculated.

要从现有的变换矩阵计算对象的旋转,可以使用atan2(t.x.y,t.x.x),其中t是Transform2D。
tanθ=对边/邻边,要计算旋转的弧度,用atan2(对边,邻边)即atan2(t.x.y,t.x.x)

在实际项目中,可以使用rotate()方法执行旋转。

变换矩阵的基础

到目前为止,我们只对x和y两个向量进行了研究,它们负责表示旋转、缩放和/或剪切(高级的,在最后介绍)。X和Y向量一起被称为变换矩阵的基础。"基础 "和 "基础向量 "这两个术语很重要,要知道。

你可能已经注意到,Transform2D实际上有三个Vector2值:x、y和origin。原点值不是基础的一部分,但它是变换的一部分,我们需要它来表示位置。从现在开始,我们将在所有的例子中跟踪原点向量。您可以将原点视为另一列,但通常最好将它完全分开。

请注意,在3D中,Godot具有用于保存基础的三个Vector3值的单独的Basis结构,因为代码可能变得复杂,并且将其与Transform分开是有意义的(Transform由一个Basis和一个额外的Vector3组成) )。

平移变换矩阵

改变原点向量称为平移变换矩阵。平移基本上是一个技术术语,用于“移动”物体,但它明确地不涉及任何旋转。

让我们通过一个示例来帮助理解这一点。和上次一样,我们从单位变换开始,只是这次我们要跟踪原点向量。
在这里插入图片描述
如果我们想让物体移动到(1,2)的位置,我们只需要将它的原点向量设置为(1,2):
在这里插入图片描述
还有一个translation()方法,该方法执行与直接添加或更改原点不同的操作。 translation()方法将使对象相对于其自身的旋转进行平移。 例如,使用Vector2.UP转换时,顺时针旋转90度的对象将向右移动。

Godot的2D使用的是基于像素的坐标,所以在实际的项目中,你会希望按几百个单位进行平移。

把它们放在一起

我们将把到目前为止提到的所有内容应用到一个转换中。 接下来,创建一个带有Sprite节点的简单项目,并使用Godot徽标作为纹理资源。

让我们将平移设置为(350,150),旋转-0.5 rad,缩放3。我已经发布了一个截图,以及重现它的代码,但是我鼓励您在不查看代码的情况下尝试重现这个截图!
在这里插入图片描述

var t = Transform2D()
# Translation
t.origin = Vector2(350, 150)
# Rotation
var rot = -0.5 # The rotation to apply.
t.x.x = cos(rot)
t.y.y = cos(rot)
t.x.y = sin(rot)
t.y.x = -sin(rot)
# Scale
t.x *= 3
t.y *= 3
transform = t # Change the node's transform to what we just calculated.

变换的实际应用

在实际项目中,通常将拥有多个Node2D或空间节点的父节点的transform中使用transform。

然而,有时手动计算我们需要的值是非常有用的。我们将介绍如何使用Transform2D或Transform来手动计算节点的变换。

变换之间的位置转换

在很多情况下,你会想把一个位置转换到一个变换中去。例如,如果你有一个相对于玩家的位置,并想找到世界(父相对)位置,或者如果你有一个世界位置,并想知道它相对于玩家的位置。

我们可以使用“ xform”方法找到相对于玩家的向量在世界空间中的定义:

# World space vector 100 units below the player.
print(transform.xform(Vector2(0, 100)))

我们可以使用 "xform_inv "方法来找到一个相对于玩家的世界空间位置:

# Where is (0, 100) relative to the player?
print(transform.xform_inv(Vector2(0, 100)))

如果您事先知道变换的位置在 (0, 0),您可以使用 “basis_xform” 或 “basis_xform_inv” 方法来代替,这些方法可以跳过处理转换。

相对于自身移动物体

一个常见的操作,特别是在3D游戏中,是相对于自身移动一个物体。例如,在第一人称射击游戏中,当你按下W时,你会希望角色向前移动(-Z轴)。

由于基本向量是相对于父对象的方向,而原点向量是相对于父对象的位置,因此我们可以简单地添加多个基本向量来相对于自身移动对象。
该代码将一个对象移动100个单位到自己的右边。

transform.origin += transform.x * 100

如果要在3D中移动,你需要将 "x "替换为 “basis.x”。

在实际项目中,你可以在3D中使用translate_object_local或者在2D中使用move_local_x和move_local_y来实现。

将变换应用到变换上

关于变换的最重要的事情之一是如何将几个变换一起使用。一个父节点的变换会影响它的所有子节点。让我们来剖析一个例子。

在这个图像中,子节点在组件名称后面有一个“2”,以区别于父节点。这么多的数字可能看起来有点让人不知所措,但请记住,每个数字都显示了两次(箭头旁边和矩阵中),而且几乎一半的数字是零。
在这里插入图片描述
这里进行的唯一转换是,父节点的缩放为(2,1),子节点的缩放为(0.5,0.5),并且两个节点都被给定了位置。
我们知道,单位矩阵(如下图),表示没有旋转和缩放。现在父节点,X(2,0),表面了x轴方向放大2倍,Y(0,1)在y轴没有缩放,保持不变,所以父节点看起来被向横拉伸,同理,子节点在x,y轴都被缩小一半。
在这里插入图片描述
而原点向量表示平移,初始时O(0,0)。父节点O(1,2),表示了相对于舞台平移到了(1,2)位置。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值