Unity3D数学第一篇:向量与点、线、面(基础篇)
Unity3D数学第二篇:旋转与欧拉角、四元数(核心变换篇)
Unity3D数学第三篇:坐标系与变换矩阵(空间转换篇)
Unity3D数学第四篇:射线与碰撞检测(交互基础篇)
Unity3D数学第五篇:几何计算与常用算法(实用算法篇)
第三篇:坐标系与变换矩阵(空间转换篇)
在 3D 游戏开发中,我们不断地在不同的“参考系”或“视角”之间切换。一个物体的“前方”可能相对于它自身,也可能相对于整个世界;鼠标点击的屏幕位置,最终要对应到 3D 世界中的某个点。这些转换的背后,离不开坐标系 (Coordinate Systems) 和 变换矩阵 (Transformation Matrices) 的强大作用。
本篇教程将深入探讨 Unity 中各种常见的坐标系,揭示它们之间的转换机制,并为你揭开变换矩阵这一“幕后英雄”的面纱。理解这些概念,将让你对 3D 空间中的一切变换拥有更深刻的洞察力。
1. 深入理解坐标系:3D 空间的“视角”
想象一个房间,你可以在房间的中心描述一件家具的位置(例如,“它在我前面两米”),也可以相对于房间的墙角来描述(例如,“它在东墙角往北三米,往西两米”)。这两种描述方式,分别对应着不同的坐标系。
在 3D 游戏世界中,存在多种相互关联的坐标系,它们服务于不同的目的。
1.1 局部坐标系 (Local Space / Object Space)
-
概念: 每个独立的 3D 物体都有其自身的局部坐标系。这个坐标系的原点通常位于物体的中心点(或称为轴心点 Pivot),而它的 X、Y、Z 轴则沿着物体自身的特定方向。
- 例如,一个汽车模型,它的局部 Y 轴通常指向它的“上方”(车顶),局部 Z 轴指向它的“前方”(车头),局部 X 轴指向它的“右方”。
-
特性: 当物体自身旋转或移动时,它的局部坐标系也跟着它一起旋转和移动。因此,一个点在物体局部坐标系中的坐标是固定不变的。
-
Unity 中的体现:
-
Transform.localPosition
:表示物体相对于其父对象的局部位置。如果物体没有父对象,则等同于transform.position
。 -
Transform.localRotation
:表示物体相对于其父对象的局部旋转。 -
Transform.localScale
:表示物体相对于其父对象的局部缩放。 -
transform.forward
、transform.up
、transform.right
:这些便捷属性返回的是物体在世界坐标系下其局部 Z、Y、X 轴的方向向量。例如,transform.forward
就是物体当前面向的世界方向。
-
1.2 世界坐标系 (World Space)
-
概念: 整个 3D 场景的全局、固定参考系。它的原点通常位于
(0, 0, 0)
,X、Y、Z 轴指向固定方向。在 Unity 中,通常:-
X 轴: 指向右方 (Red Axis)
-
Y 轴: 指向上方 (Green Axis)
-
Z 轴: 指向前方 (Blue Axis)
-
-
特性: 世界坐标系是所有物体共享的“大地图”。所有物体最终都会被放置在世界坐标系中的某个位置和姿态。
-
Unity 中的体现:
-
Transform.position
:表示物体在世界坐标系中的位置。 -
Transform.rotation
:表示物体在世界坐标系中的旋转。 -
Transform.lossyScale
:表示物体在世界坐标系中的最终缩放(包含了父级的缩放影响)。
-
1.3 父子关系与层级变换
在 Unity 中,游戏对象可以有父子关系。当一个对象成为另一个对象的子对象时,它的局部坐标系就变成了相对于其父对象的坐标系。子对象的任何变换都会受到父对象变换的影响。
-
示例: 如果一个手臂是身体的子对象,当身体旋转时,手臂也会跟着旋转(因为手臂的局部坐标系随着身体的世界坐标系一起旋转了)。而手臂自身的局部旋转,则是相对于它当前所处的“父级空间”进行的。
-
Transform.SetParent()
方法可以用来建立和解除父子关系,这在运行时动态组合对象时非常有用,例如拾取武器或附件。
为什么要区分局部坐标系和世界坐标系?
理解这些坐标系的关键在于它们处理问题的方式:
-
局部坐标系方便描述物体自身的属性(例如,我向前走,就是沿着我的局部 Z 轴),因为它不受外部环境变化的影响。
-
世界坐标系方便描述物体在整个场景中的绝对位置和关系(例如,哪个物体离世界原点最近),因为它是一个统一的参考标准。
在游戏开发中,我们经常需要在两者之间进行转换。
1.4 屏幕坐标系 (Screen Space)
-
概念: 你的游戏屏幕(或窗口)上的 2D 像素坐标系。
-
原点通常在屏幕的左下角
(0, 0)
。 -
X 轴向右延伸,Y 轴向上延伸。
-
Z 轴通常表示深度,即点离摄像机的距离。
-
-
特性: 像素单位,直接对应屏幕上的视觉呈现。
-
Unity 中的体现:
-
Input.mousePosition
:鼠标在屏幕上的像素坐标。 -
Input.GetTouch(0).position
:触控点在屏幕上的像素坐标。 -
UI Canvas 的
Screen Space - Overlay
模式下的 UI 元素位置。
-
1.5 视口坐标系 (Viewport Space)
-
概念: 相机在渲染时看到的**“视口”**的 2D 归一化坐标系。
-
原点在视口的左下角
(0, 0)
。 -
右上角是
(1, 1)
。 -
X、Y 值范围是
0.0
到1.0
。 -
Z 轴同样表示深度,即点离摄像机的距离。
-
-
特性: 独立于屏幕分辨率。无论屏幕多大,视口左下角始终是
(0,0)
,右上角始终是(1,1)
。这对于判断物体是否在屏幕内非常有用。 -
Unity 中的体现:
-
Camera.WorldToViewportPoint()
:世界坐标转视口坐标。 -
Camera.ViewportToWorldPoint()
:视口坐标转世界坐标。 -
Camera.ViewportToScreenPoint()
:视口坐标转屏幕坐标。
-
1.6 其他重要但无需深究的坐标系
-
摄像机空间 (Camera Space / View Space): 以摄像机自身为原点和轴向的 3D 坐标系。所有物体在渲染前都会被转换到这个空间。
-
裁剪空间 (Clip Space): 在摄像机空间之后,点被投影到一个立方体(归一化设备坐标,NDC)中。这个空间用于剔除视锥体外的点。
-
归一化设备坐标 (Normalized Device Coordinates - NDC): 经过裁剪空间后,所有可见点被映射到一个标准的立方体中,X, Y, Z 轴范围都是
[-1, 1]
。
这些更深层次的坐标系主要在图形渲染管线内部使用,作为开发者,通常只需要理解它们的存在和作用流程,而无需手动操作它们。了解它们能让你更好地理解 3D 世界最终如何映射到 2D 屏幕上。
2. 坐标系转换:在不同“视角”之间穿梭
在游戏开发中,我们经常需要在不同坐标系之间进行点的转换,例如将一个世界坐标的点转换为局部坐标,或者将鼠标的屏幕坐标转换为 3D 世界中的点击点。Unity 提供了方便的 API 来实现这些转换。
2.1 局部坐标系与世界坐标系之间的转换
这些方法都可以在任何 Transform
组件上调用。
-
Transform.TransformPoint(Vector3 position)
:局部点转世界点-
将一个在当前
Transform
的局部坐标系中定义的点 (position
) 转换到世界坐标系中。 -
应用: 计算角色前方 N 米处的世界坐标、计算子弹从枪口(作为局部点)射出的世界起始位置。
C#
// Unity // 假设你在物体 A 上,想知道物体 A 的局部坐标 (0, 0, 5) 在世界中的位置 Vector3 localPoint = new Vector3(0, 0, 5); // 物体 A 前方 5 米处 Vector3 worldPoint = transform.TransformPoint(localPoint); Debug.Log($"局部点 {localPoint} 在世界中是 {worldPoint}");
-
-
Transform.InverseTransformPoint(Vector3 position)
:世界点转局部点-
将一个在世界坐标系中定义的点 (
position
) 转换到当前Transform
的局部坐标系中。 -
应用: 判断一个世界中的点相对于物体的具体方位(例如,敌人相对于我在哪里)、计算局部碰撞点。
C#
// Unity // 假设你想知道世界坐标 (10, 0, 0) 相对于当前物体的位置 Vector3 worldPoint = new Vector3(10, 0, 0); Vector3 localPoint = transform.InverseTransformPoint(worldPoint); Debug.Log($"世界点 {worldPoint} 相对于我是在 {localPoint}");
-
-
Transform.TransformDirection(Vector3 direction)
:局部方向转世界方向-
将一个在当前
Transform
的局部坐标系中定义的方向向量 (direction
) 转换到世界坐标系中。 -
重要: 这个方法只处理旋转,不考虑平移和缩放。它将局部方向向量的方向转换到世界空间,但不会改变其长度。
-
应用: 获取物体当前的世界前方 (
transform.TransformDirection(Vector3.forward)
等同于transform.forward
),获取物体自身某个方向在世界中的表示。
C#
// Unity // 获取当前物体局部右方在世界中的方向 Vector3 localRight = Vector3.right; Vector3 worldRight = transform.TransformDirection(localRight); Debug.Log($"我的局部右方在世界中是 {worldRight}");
-
-
Transform.InverseTransformDirection(Vector3 direction)
:世界方向转局部方向-
将一个在世界坐标系中定义的向量 (
direction
) 转换到当前Transform
的局部坐标系中。 -
同样,只处理旋转,不考虑平移和缩放。
-
应用: 判断世界中的某个力或方向向量对于物体自身来说是哪个方向(例如,世界重力对于斜坡上的角色来说是哪个局部方向)。
C#
// Unity // 假设你想知道世界坐标的 Vector3.up (世界向上) 对于当前物体是哪个局部方向 Vector3 worldUp = Vector3.up; Vector3 localUp = transform.InverseTransformDirection(worldUp); Debug.Log($"世界向上对于我来说是局部 {localUp}");
-
-
Transform.TransformVector(Vector3 vector)
和Transform.InverseTransformVector(Vector3 vector)
:- 这两个方法与
TransformDirection
类似,但它们会考虑缩放。当处理法线(需要逆转置矩阵)或非单位长度的向量时,需要特别注意。在大多数情况下,处理方向用TransformDirection
即可,除非你明确需要考虑缩放对向量的影响。
- 这两个方法与
2.2 世界坐标系与屏幕/视口坐标系之间的转换
这些方法通常在 Camera
组件上调用。
-
Camera.WorldToScreenPoint(Vector3 position)
:世界点转屏幕点-
将 3D 世界坐标系中的一个点 (
position
) 转换到 2D 屏幕坐标系中。返回的Vector3
的x
和y
是像素坐标,z
是该点距离摄像机的深度。 -
应用:
-
将 3D 物体在屏幕上方的位置显示一个血条 UI。
-
判断一个 3D 物体是否在屏幕内(检查
x
,y
是否在0
到Screen.width
/Screen.height
之间,且z > 0
)。
-
C#
// Unity public Transform target3DObject; void Update() {Vector3 screenPos = Camera.main.WorldToScreenPoint(target3DObject.position);Debug.Log($"3D 物体 {target3DObject.name} 在屏幕上的位置是 {screenPos}");// 判断是否在屏幕内:if (screenPos.z > 0 && screenPos.x >= 0 && screenPos.x <= Screen.width && screenPos.y >= 0 && screenPos.y <= Screen.height) {Debug.Log("物体在屏幕内!");} else {Debug.Log("物体在屏幕外!");} }
-
-
Camera.ScreenToWorldPoint(Vector3 position)
:屏幕点转世界点-
将 2D 屏幕坐标系中的一个点 (
position
) 转换到 3D 世界坐标系中。 -
关键: 由于 2D 屏幕点缺少深度信息,你必须手动为
position.z
赋值,才能确定它在 3D 空间中的深度。 -
应用:
-
点击屏幕获取 3D 世界中的一个点(例如,点击地面进行移动)。
-
将 UI 元素的位置映射到 3D 空间中的特定深度。
-
C#
// Unity // 鼠标点击屏幕,获取点击处在世界中的位置 void Update() {if (Input.GetMouseButtonDown(0)) {Vector3 mouseScreenPos = Input.mousePosition;// 必须提供 Z 深度信息!这里假设我们想在相机前方 10 米处获取世界点mouseScreenPos.z = 10f; // 这里的 z 是指离相机近裁剪面的距离Vector3 worldClickPoint = Camera.main.ScreenToWorldPoint(mouseScreenPos);Debug.Log($"鼠标点击屏幕 {Input.mousePosition} 对应世界点 {worldClickPoint}");} }
注意: 如果你需要精确点击 3D 物体表面,通常会结合射线检测 (Raycasting) 来获取深度,这比固定
z
值更精确。我们会在第四篇详细讲解。 -
-
Camera.WorldToViewportPoint(Vector3 position)
:世界点转视口点-
将 3D 世界坐标系中的一个点 (
position
) 转换到 2D 视口坐标系中。返回的Vector3
的x
和y
范围是0.0
到1.0
,z
是深度。 -
应用:
-
判断物体是否在屏幕内(通常比
WorldToScreenPoint
更方便,因为它不依赖Screen.width/height
)。 -
创建位于屏幕相对位置的 UI 元素(例如,血条总在敌人上方,但不能超出屏幕)。
-
C#
// Unity public Transform targetObject; void Update() {Vector3 viewportPos = Camera.main.WorldToViewportPoint(targetObject.position);if (viewportPos.z > 0 && viewportPos.x >= 0 && viewportPos.x <= 1 && viewportPos.y >= 0 && viewportPos.y <= 1) {Debug.Log("物体在视口内!");} else {Debug.Log("物体在视口外!");} }
-
-
Camera.ViewportToWorldPoint(Vector3 position)
:视口点转世界点-
将 2D 视口坐标系中的一个点 (
position
) 转换到 3D 世界坐标系中。 -
同样需要手动提供
position.z
深度。 -
应用: 将一个位于屏幕中央(
0.5, 0.5
)的 UI 元素映射到 3D 世界中的某个深度。
-
3. 变换矩阵:空间转换的幕后英雄
在所有的坐标系转换背后,真正执行“数学魔法”的是变换矩阵 (Transformation Matrices)。虽然 Unity 为我们封装了大多数底层矩阵操作,但理解它们的存在和作用原理,能让你更深刻地理解 3D 图形学,并在遇到复杂问题时游刃有余。
3.1 什么是变换矩阵?
-
概念: 在 3D 图形中,一个矩阵 (Matrix) 是一个矩形排列的数字集合。一个 4x4 的矩阵可以同时表示 3D 空间中的平移 (Translation)、旋转 (Rotation) 和缩放 (Scaling) 这三种基本变换。
-
矩阵乘法: 当一个 3D 点(表示为齐次坐标下的向量)与一个变换矩阵相乘时,就可以得到变换后的新点。
- 例如,
NewPoint = Matrix * OldPoint
。
- 例如,
-
关键特性:不满足交换律!
MatrixA * MatrixB
不等于MatrixB * MatrixA
。这意味着变换的顺序至关重要。先旋转再平移,和先平移再旋转,会得到完全不同的结果。这解释了为什么 Unity 中Transform
组件的position
和rotation
操作是分开的,以及为什么父子变换的顺序会影响最终结果。
3.2 矩阵如何工作?(简要原理)
一个 4x4 的变换矩阵通常结构如下:
| Sx 0 0 Tx |
| 0 Sy 0 Ty |
| 0 0 Sz Tz |
| 0 0 0 1 |
-
其中
Sx, Sy, Sz
控制缩放。 -
矩阵左上角的 3x3 部分(包含 0 和对角线上的值)控制旋转。
-
Tx, Ty, Tz
控制平移。
当一个 3D 点 (x, y, z, 1)
(这里的 1
是齐次坐标的约定)与这个矩阵相乘时,就能同时实现平移、旋转和缩放。
3.3 MVP 矩阵:3D 世界到 2D 屏幕的渲染管线
在 3D 渲染管线中,一个 3D 模型从其自身定义到最终呈现在屏幕上,会经历一系列的坐标系转换,这些转换都由特定的变换矩阵完成。这就是著名的 MVP 矩阵。
-
模型矩阵 (Model Matrix / World Matrix):局部空间 -> 世界空间
-
作用: 将 3D 模型在它自己的局部坐标系中定义的顶点,转换到世界坐标系中。
-
每个物体都有一个模型矩阵,它由该物体的
Transform.position
、Transform.rotation
和Transform.localScale
决定。 -
例如,一个角色模型,它的模型矩阵决定了它在世界中的位置、方向和大小。
-
-
视图矩阵 (View Matrix):世界空间 -> 摄像机空间
-
作用: 将世界坐标系中的所有点,转换到摄像机(或观察者)的局部坐标系中。这相当于从摄像机的角度“看”世界。
-
视图矩阵实际上是摄像机自身变换矩阵的逆矩阵。
-
它模拟了摄像机的位置和朝向。
-
-
投影矩阵 (Projection Matrix):摄像机空间 -> 裁剪空间/NDC 空间
-
作用: 将摄像机空间中的 3D 点,投影到 2D 屏幕上,并进行深度挤压和透视变换。
-
它定义了摄像机的视野 (Field of View - FOV)、近裁剪面 (Near Clip Plane) 和远裁剪面 (Far Clip Plane)。
-
两种主要类型:
-
透视投影 (Perspective Projection): 模拟人眼看世界的效果,远的物体小,近的物体大,有透视感。
-
正交投影 (Orthographic Projection): 没有透视感,所有物体大小不变,常用于 2D 游戏、工程视图或特殊的 3D 效果。
-
-
投影矩阵的输出通常是裁剪空间,然后通过除以 W 分量(透视除法)进入归一化设备坐标 (NDC) 空间,X、Y、Z 轴范围都是
[-1, 1]
。
-
渲染管线中的流程:
顶点数据 (局部坐标) -> 模型矩阵 -> 世界坐标 -> 视图矩阵 -> 摄像机空间 -> 投影矩阵 -> 裁剪空间 -> 透视除法 -> NDC 空间 -> 视口变换 -> 屏幕坐标 -> 最终渲染
为什么要理解 MVP 矩阵?
虽然 Unity 帮你处理了所有这些复杂的矩阵乘法,但了解 MVP 矩阵的工作原理,能让你:
-
理解 3D 渲染的底层逻辑: 当你在 Shader 中处理顶点或像素时,你是在哪个坐标系下操作?这直接影响你的计算结果。
-
调试渲染问题: 当物体显示不正确时,可能是哪个矩阵出了问题?
-
实现自定义渲染效果: 如果你需要编写自定义的渲染管道或着色器,对 MVP 矩阵的理解是必不可少的。
4. 坐标系与变换矩阵在 Unity 中的高级应用
理解坐标系和矩阵的工作方式后,我们可以解决一些实际的游戏开发问题。
4.1 鼠标点击精确拾取 3D 物体(结合射线)
这是最常见的应用之一。鼠标点击的 2D 屏幕点,如何找到它在 3D 世界中击中的物体?
C#
// Unity
void Update() {if (Input.GetMouseButtonDown(0)) {// 1. 将鼠标的屏幕坐标转换为一条射线// Camera.main.ScreenPointToRay() 会根据相机位置和方向,从屏幕点发射一条射线Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);// 2. 声明一个 RaycastHit 结构体来存储射线检测结果RaycastHit hit;// 3. 执行射线检测// Physics.Raycast 会沿着射线方向检测碰撞体if (Physics.Raycast(ray, out hit, 100f)) { // 100f 是射线的最大检测距离Debug.Log($"鼠标点击了物体: {hit.collider.gameObject.name},在世界坐标: {hit.point}");// 你可以对 hit.collider.gameObject 进行操作,例如选中、高亮} else {Debug.Log("没有点击到任何 3D 物体。");}}
}
这里的核心就是将 2D 的屏幕坐标,通过摄像机的投影信息反推回 3D 世界中的一条射线。
4.2 UI 元素跟随 3D 物体(血条、名称板)
让一个 UI 元素(例如角色的血条或名称板)始终位于其对应的 3D 角色上方。
C#
// Unity
public Transform target3DCharacter; // 要跟随的 3D 角色
public RectTransform uiElementRectTransform; // 要跟随的 UI 元素的 RectTransformvoid Update() {if (target3DCharacter == null || uiElementRectTransform == null || Camera.main == null) return;// 1. 将 3D 角色头顶的世界坐标转换为屏幕坐标// 假设血条在角色头顶上方 2 个单位处Vector3 worldPositionAboveCharacter = target3DCharacter.position + Vector3.up * 2f;Vector3 screenPosition = Camera.main.WorldToScreenPoint(worldPositionAboveCharacter);// 2. 判断物体是否在摄像机前方且在屏幕内(防止血条显示在屏幕外或背后)if (screenPosition.z > 0 &&screenPosition.x >= 0 && screenPosition.x <= Screen.width &&screenPosition.y >= 0 && screenPosition.y <= Screen.height) {uiElementRectTransform.gameObject.SetActive(true); // 显示 UI 元素// 3. 将屏幕坐标转换为 UI Canvas 的 RectTransform 坐标// UI 坐标通常是相对于 Canvas 的,需要特殊处理Vector2 localUIPos;RectTransformUtility.ScreenPointToLocalPointInRectangle(uiElementRectTransform.parent as RectTransform, // UI 元素的父级 Canvas RectTransformscreenPosition,Camera.main.WorldToScreenPoint(Camera.main.transform.position).z, // 假设 Z 深度相同out localUIPos);// 如果 Canvas 是 Screen Space - Camera 模式,第三个参数是 Camera.main// 如果 Canvas 是 Screen Space - Overlay 模式,第三个参数是 null// 鉴于目前是 Screen Space - Overlay,直接使用 screenPosition 即可// 或者更简单的,直接赋值,Unity UI 系统会处理好:uiElementRectTransform.position = screenPosition;} else {uiElementRectTransform.gameObject.SetActive(false); // 隐藏 UI 元素}
}
注意: 对于 Unity UI,RectTransformUtility.ScreenPointToLocalPointInRectangle
通常用于将屏幕点转换为 Canvas 内部的局部坐标,这取决于你的 Canvas 渲染模式(Screen Space - Overlay
、Screen Space - Camera
或 World Space
)。这里为了简化,如果 Canvas 是 Screen Space - Overlay
模式,直接将 uiElementRectTransform.position = screenPosition;
即可,Unity 会处理其与屏幕坐标的映射。
4.3 相机边界限制
限制相机只能在特定区域内移动,或者当玩家靠近屏幕边缘时,相机自动平移。
C#
// Unity
public Transform player;
public float edgeOffset = 0.1f; // 屏幕边缘触发区域 (0-1 范围)
public float cameraMoveSpeed = 5f;void Update() {// 1. 将玩家位置转换为视口坐标Vector3 viewportPos = Camera.main.WorldToViewportPoint(player.position);Vector3 cameraMoveDirection = Vector3.zero;// 2. 检查玩家是否接近视口边缘if (viewportPos.x < edgeOffset) {cameraMoveDirection += Vector3.left;} else if (viewportPos.x > 1f - edgeOffset) {cameraMoveDirection += Vector3.right;}if (viewportPos.y < edgeOffset) {cameraMoveDirection += Vector3.down;} else if (viewportPos.y > 1f - edgeOffset) {cameraMoveDirection += Vector3.up;}// 3. 移动相机Camera.main.transform.position += cameraMoveDirection.normalized * cameraMoveSpeed * Time.deltaTime;
}
这里利用视口坐标系的归一化特性,方便地判断玩家相对于屏幕边缘的位置。
5. 常见面试题与深入思考
5.1 面试问答
-
问:请解释局部坐标系和世界坐标系的区别。在 Unity 中,当物体存在父子关系时,
transform.position
和transform.localPosition
有何不同?-
解析:
-
局部坐标系: 物体自身的坐标系,原点在物体轴心,轴向与物体自身方向一致。描述物体自身的几何数据。
-
世界坐标系: 整个场景的全局、固定坐标系,原点在
(0,0,0)
,轴向固定。描述物体在整个世界中的绝对位置。 -
transform.position
: 表示物体在世界坐标系中的绝对位置。 -
transform.localPosition
: 表示物体在父对象局部坐标系中的相对位置。如果物体没有父对象,则localPosition
等同于position
。当父对象移动或旋转时,子对象的localPosition
不变,但其position
会随父对象而改变。
-
-
-
问:如何在 Unity 中将一个世界坐标的点转换到某个物体的局部坐标系?请举例说明应用场景。
-
考察点:
InverseTransformPoint
的使用及其应用。 -
解析: 使用
transform.InverseTransformPoint(worldPoint)
方法。-
应用场景:
-
判断一个世界中的目标点相对于当前物体的方位。例如,一个 AI 想要知道玩家在它自身的左侧还是右侧、前方还是后方,就需要将玩家的世界位置转换为 AI 的局部坐标,然后判断 X、Z 分量的正负。
-
获取一个物体被击中时,局部碰撞点在哪里,这在制作特效(例如粒子效果从特定部位发出)或计算局部伤害时很有用。
-
-
-
-
问:请简要描述 MVP 矩阵在 3D 渲染管线中的作用。它们分别负责什么转换?
-
考察点: 对 3D 图形渲染基础流程的理解,矩阵的抽象作用。
-
解析:
-
MVP 矩阵是 3D 世界中的点最终如何被渲染到 2D 屏幕上的关键。
-
模型矩阵 (Model Matrix): 将模型自身的局部坐标转换为世界坐标。它决定了物体在世界中的位置、旋转和缩放。
-
视图矩阵 (View Matrix): 将世界坐标转换为摄像机(观察者)的局部坐标。它模拟了摄像机的位置和朝向。
-
投影矩阵 (Projection Matrix): 将摄像机空间中的 3D 点投影到 2D 屏幕(归一化设备坐标),并进行透视变换和裁剪。它定义了摄像机的视野 (FOV)、近远裁剪面,决定了最终画面的透视感或正交感。
-
-
总结: 模型矩阵定位物体;视图矩阵选择观察视角;投影矩阵定义如何将 3D 世界映射到 2D 屏幕。
-
-
问:如何在 Unity 中让一个 UI 元素(例如血条)始终跟随一个 3D 角色显示在它的头顶?你会用到哪些坐标系转换?
-
考察点: 综合运用
WorldToScreenPoint
和 UI 坐标系转换的理解。 -
解析:
-
首先,获取 3D 角色头顶的世界坐标。
-
使用
Camera.main.WorldToScreenPoint()
将这个世界坐标转换为屏幕坐标(像素坐标)。 -
然后,将这个屏幕坐标赋值给 UI 元素的
RectTransform.position
。 -
额外考虑: 需要判断
WorldToScreenPoint
返回的z
值是否大于 0(在摄像机前方),以及x, y
是否在屏幕范围内,以决定是否显示 UI 元素,避免显示在屏幕外或角色背后。对于不同 Canvas 渲染模式下的 UI 定位,可能还需要额外的RectTransformUtility
转换。
-
-
总结与展望
本篇教程深入探讨了 3D 游戏开发中至关重要的坐标系和变换矩阵:
-
我们详细区分了局部坐标系、世界坐标系、屏幕坐标系和视口坐标系,理解了它们各自的用途。
-
掌握了 Unity 中局部与世界、世界与屏幕/视口之间进行转换的各种 API,让你能够灵活处理位置和方向的转换。
-
揭示了变换矩阵的底层原理,包括其表示平移、旋转、缩放的能力,以及不满足交换律的重要性。
-
深入理解了 MVP 矩阵 (模型、视图、投影矩阵) 在 3D 渲染管线中的核心作用,让你对 3D 画面如何呈现在屏幕上有了更清晰的认识。
对坐标系和变换的理解,是你在 Unity 中构建复杂层级结构、实现相机逻辑、处理 UI 交互以及深入图形渲染的基石。
在下一篇《射线与碰撞检测(交互基础篇)》中,我们将结合你已经掌握的向量、旋转、坐标系知识,深入学习如何在 3D 世界中进行精确的交互检测,例如鼠标点击拾取、子弹命中判断以及各种物理碰撞。
现在,你对坐标系和变换矩阵的概念是否已经更加清晰了呢?在 Unity 开发中,你是否曾遇到过因坐标系混淆而导致的问题?
Unity3D数学第一篇:向量与点、线、面(基础篇)
Unity3D数学第二篇:旋转与欧拉角、四元数(核心变换篇)
Unity3D数学第三篇:坐标系与变换矩阵(空间转换篇)
Unity3D数学第四篇:射线与碰撞检测(交互基础篇)
Unity3D数学第五篇:几何计算与常用算法(实用算法篇)