1.1 拥抱3D
本节我们会对3D(三维)做一个基本介绍,从坐标系统到3D模型如何渲染,我们均只介绍基础知识,以确保你能够完全理解。阅读本节之后,你将能够理解Unity如何显示各种对象。
1.1.1 坐标系统
不是所有3D程序中的3D坐标系统都是一样的!Unity是+y朝上的左手坐标系,左手坐标系和右手坐标系的区别如图1.1所示。
当你使用这些坐标系统时,会发现对象的位置是用括号括起来的三个值组成的数组表示的,像这样:
图1.1 坐标系统
这三个数字分别表示(x,y,z)。使用这样的表达方式是一个好习惯,因为在编写位置脚本时,代码也使用非常相似的语法。当我们讨论位置时,通常指的是数字内容生成器(Digital Content Creator,DCC)内部的变换(Transform)。在Unity中,Transform包含位置、旋转和缩放。
现在我们明白了世界坐标系(x,y,z),并且这几个坐标值均从0开始,用(0, 0, 0)表示原点。在图1.2中,3条线相交的地方就是世界坐标系中的(0, 0, 0)。图中的立方体有自己的Transform,包括对象的Transform、旋转和缩放。请记住,Transform保存局部的位置、旋转和缩放。全局Transform是根据对象的层级关系计算出来的。
图1.2 3D坐标系
图1.2中的立方体在坐标(1, 1.5, 2)处。这叫作世界空间(world space),因为立方体的Transform是通过以(0, 0, 0)为原点的世界坐标系表示的。
现在我们知道立方体的Transform与世界坐标系中的(0, 0, 0)相关,我们来看一下用于描述局部空间的父子关系。在图1.3中,球体是立方体的子对象。球体的局部位置相对于立方体是(0, 1, 0)。有趣的是,如果你现在移动立方体,球体将跟着移动,因为它只会相对立方体偏移,并且球体的Transform相对于立方体一直是(0, 1, 0)。
图1.3 世界空间与局部空间
1.1.2 矢量系统
从传统来讲,矢量是多个具有方向的元素的单位。在3D设置中,Vector3看起来与我们目前使用的坐标系非常相似。(0, 0, 0)就是一个Vector3值!许多游戏元素和逻辑的解决方案中都会用到矢量。通常,开发人员会对矢量进行归一化,这样,其大小(magnitude)将始终等于1。这使得开发人员可以非常轻松地处理数据,因为0是起点,0.5是中点,1是矢量的终点。
1.1.3 摄像机
摄像机是非常有用的组件,能够向我们传递视野,让玩家体验我们试图传达给他们的信息。如你所料,就像层级结构中的所有游戏对象(将在本章后面讲述)一样,摄像机也有一个Transform。可以改变摄像机的几个参数来获得不同的视觉效果。
不同的游戏元素和类型用不同的方式使用摄像机。例如,《生化危机》游戏中使用静态摄像机给人一种紧张感,让玩家不知道窗外或拐角处有什么,而《古墓丽影》则在玩家的角色劳拉穿过洞穴时将摄像机拉近,给人一种近距离感和情感上的认知,在狭窄的空间里,她看起来不太高兴。
摄像机对于用户体验来说至关重要。花时间练习一下摄像机,然后学习构图的概念,可以最大限度地触动玩家体验时的情感。
1.1.4 面、边、顶点和网格
3D对象由多个部分组成,如图1.4所示。绿色的点表示顶点,是相对于世界坐标系(0, 0, 0)的空间点位,每个对象都有一些顶点以及每个顶点对应的连接部分。
互相连接的两个顶点构成一条边,图中用红线表示。当三条或四条边相应连接成三角形或四边形时,就会形成一个面。四边形有时在未连接到其他面时称为平面。当所有这些部分都放在一起时,就形成了一个网格。
图1.4 面、边、顶点和网格
1.1.5 材质、纹理和着色器
现在,你知道了所有DCC工具中的网格是由什么组成的,我们来看看Unity如何显示这个网格。在底层是一个着色器,着色器可以被看作一小段程序,它们有自己的语言并在GPU上运行,因此Unity可以在屏幕上渲染场景中的对象。你可以将着色器看作要创建的材质的大型模板。
再往上一层是材质,材质是着色器定义的要处理的一组属性,用于显示对象的外观。每种渲染管线都有单独的着色器:内置渲染管线、通用渲染管线(URP)或高清渲染管线。我们在本书中使用第二种——URP,它也是使用最广泛的。
图1.5所示是使用URP的标准光照(Standard Lit)着色器的材质示例。这个着色器可以操作表面选项(Surface Options)、表面输入(Surface Inputs)以及一些高级(Advanced)选项。现在我们来介绍一下基础贴图(Base Map),即Surface Inputs部分的第一项。Base Map在这里指漫反射/反射率和着色的组合。漫反射/反射率用于定义应用到表面的基色,在本例中是白色。
如果把纹理放到Base Map上,不管是通过把纹理拖到Base Map框①标注的正方形上面,还是单击框②标注的蓝色圆圈,都可以在需要调整颜色时给表面着色。
图1.6展示了不同立方体的外观,它们分别是有颜色的、添加了纹理的、既添加了纹理又有颜色的。我们会循序渐进地讲解材质、着色器和纹理的更多功能。
图1.5 基础材质属性
图1.6 颜色和纹理
纹理可以为3D模型提供令人难以置信的细节。
创建纹理时,分辨率是一个重要考虑因素。对于分辨率,需要理解的第一部分内容是“2的幂”这个尺寸。2的幂可能是2、4、8、16、32、64、128、256、512、1024、2048、4096等。
这些数字代表像素的宽度和高度值。在某些情况下,你可能需要把宽和高放在一起,只要它们同为2的幂即可。比如:
❑ 256×256
❑ 1024×1024
❑ 256×1024(不太常见,但也是有效的数值)
关于分辨率的第二个考虑因素是分辨率的大小本身。确定分辨率最简单的方法是考虑3D对象在屏幕上会有多大。如果你的屏幕分辨率为1920×1080,则表示1920像素宽乘以1080像素高。如果目标对象最多只占屏幕的10%并且很少会看到对象的细节,则可以考虑使用256×256的纹理。相比之下,如果你正在制作一个有情感的、人物角色驱动的游戏,角色的情绪和面部表情都很重要,那么可能需要在过场动画中使用4096×4096或4K的纹理,但只需要在面部使用即可。
1.1.6 刚体物理
Unity假设所有游戏对象都不需要在每一帧均进行物理计算。Unity使用Nvidia的PhysX引擎进行物理模拟。要想获得计算出的物理反馈,需要在游戏对象中添加一个刚体(Rigidbody)组件。
将Rigidbody组件添加到游戏对象中,就可以在检视器中添加一些属性到游戏对象上了,如图1.7所示。
图1.7 Rigidbody组件
一个Unity的单位质量(Mass)等于1kg。这个值会影响碰撞时的物理反应。阻力(Drag)单元会增加摩擦力,随着时间的推移降低速度。角度阻力(Angular Drag)与之类似,但只会影响旋转速度。使用重力(Use Gravity)可以开启或者关闭重力效果,值等于标准地球重力(0,-9.81, 0),以使质量是有意义的!有时你可能不想使用地球重力,那么可以更改物理设置以设置是否使用重力。
第7章将详细介绍刚体,我们在创建人物角色、环境和可交互玩法时也会用到它。
1.1.7 碰撞检测
如果一个游戏对象有刚体但没有任何碰撞器,那么它将不能充分利用物理特性,并且在使用重力的情况下只会从世界空间坠落。Unity有多个碰撞器可以使用,挑选符合你的游戏需求的即可。在图1.8中,你会发现有单独用于2D游戏的碰撞器。2D环境与3D环境使用不同的物理系统。如果你的游戏中只使用2D,要保证使用的是2D碰撞器。
图1.8 碰撞器组件选项
你也可以向对象添加多个碰撞器(使用图1.8所示的这些基本选项),但请选择最适合游戏对象的形状的那些。通常会把一个空游戏对象作为主对象的子对象,然后在子对象上附加碰撞器,这样可以方便地调整碰撞器的Transform。我们将在第4章和第5章中具体实践。