当前位置:   article > 正文

Unity 3D脚本编程与游戏开发(1.3)

Unity 3D脚本编程与游戏开发(1.3)

第2章 Unity基本概念与脚本编程

        如果将所有流⾏的游戏引擎做⼀个对⽐,会发现Unity所采⽤的概念架构极其简洁。在精简的基本概念之上提供丰富的功能是Unity引擎的⼀⼤特点,也是它如此流⾏的重要原因之⼀。
        通过本章的学习,读者应该能掌握Unity最重要的基本概念,以及基本的脚本编写⽅法,对“简洁的概念架构”有深⼊的理解。
        值得注意的是,本章是本书的核⼼。⼀⽅⾯,只需要掌握本章的内容,再加上⼀些开发技巧,理论上就已经可以制作出各种各样的⼩游戏了;另⼀⽅⾯,未来所编写的Unity脚本代码都会⽤到本章所讲解的基础知识,万变不离其宗。

2.1 Unity基本概念

⽤Unity创建的游戏是由⼀个或多个场景组成的,默认Unity会打开⼀个场景,如图2-1所⽰。

        在游戏开发时,绝⼤部分操作都是在某⼀个场景中进⾏的,因此⼀开始不⽤关⼼多个场景之间的关系,只需关⼼在⼀个场景之内发⽣的事情。实际上,关键的概念只有GameObject(游戏物体)、Component(组件)和⽗⼦关系3个。

2.1.1 游戏物体和组件

        “⼀个游戏场景中有很多物体”这句话很直观地表达了意思,Unity也正是这么设计的。                Unity将游戏中的物体称为GameObject,即游戏物体。⼀个场景中可以包含任意数量的游戏物体。在第1章创建过平⾯、球体、⽴⽅体等游戏物体,⽽组件则是实现功能特性的单元。要理解组件的作⽤,⾸先需要知道游戏物体只是⼀个空的容器,专门⽤来存放组件。这⼀点不太容易理解,下⾯将进⾏详细解释。
        ⼀个Unity场景可以看作⼀个虚拟世界,虚拟世界中可以有很多物体。在虚拟世界中,每个物体只有⾃⾝的名称、标签等基本信息。除此以外,物体的所有重要或不重要的性质,包括外形、颜⾊、⽗⼦关系、重量、碰撞体、脚本功能等,甚⾄包括物体的位置本⾝,都需要⽤组件表⽰。换句话说,如果虚拟世界中只有⼏个游戏物体,没有任何组件,那么这个世界就会空空如也。⽽我们仅仅知道有物体存在,却连物体的位置都⽆法表⽰。
        游戏物体除了名称和标签等基本信息外,本⾝不具备任何可直观感受到的特性,但它拥有最关键的⼀个功能——挂载组件。“挂载”的意思是让物体拥有这个组件,即让组件附属于某个物体。能够挂载组件是游戏物体最主要的功能。

        ⼀个物体可以挂载任意多个组件,只要挂载合适的组件,物体就会“摇⾝⼀变”,变成游戏中的图⽚、UI界⾯、模型或摄像机等。多个组件共同作⽤,就能组成⼀个有功能性的物体;⽽多个有功能性的物体⼀起放在场景⾥,就能组成丰富多彩的游戏世界,如图2-2所⽰。

        Unity中的组件繁多,在2.1.4⼩节会列举说明。这⾥先举⼏个具体的常⻅物体的例⼦,分析它们的组件,让读者对组件功能有⼀个⼤概的认识。
        1. 空物体
        前⾯提到,如果游戏物体没有组件,我们就连物体的位置都⽆法表⽰。为避免出现这种尴尬的情况,Unity规定任何物体必须有且只有⼀个Transform组件。也就是说,Transform组件和物体⼀⼀对应。
        因此,所谓的“空物体”就是只包含⼀个Transform组件的物体。在Hierarchy窗⼝空⽩处单击⿏标右键打开菜单,选择Create Empty选项创建⼀个空物体,如图2-3所⽰。

        空物体没有外形,因此在场景中⽆法看到它的外观,但是可以对它使⽤位移、旋转等⼯具,改变它的位置及其在空间中的朝向。
        出乎意料的是,空物体是游戏开发中最常⽤的物体类型。虽然看起来改变空物体的位置是在做⽆⽤功,但实际上是有意义的,只是它的⽤途要结合“⽗⼦关系”才能真正发挥出来。
        有⼀个恰当的类⽐:计算机中的⽂件夹既不能直接保存⽂档数据,⼜不能直接当作程序运⾏,但是⽇常使⽤计算机⼜离不开它。Unity中的空物体也具有相似的作⽤,但它的功能⽐⽂件夹要多,之后讲解到“⽗⼦关系”时,⼀切就都清楚了。
        2. 球体等3D原型物体
        与创建空物体的⽅法类似,再创建⼀些球体(Sphere)、⽴⽅体(Cube)、平⾯(Plane)等,它们有⼀个不常⽤的名字叫作原型物体(Primitive)。原型物体还包含㬵囊体(Capsule)、圆柱体(Cylinder)、竖直⼩平⾯(Quad),如图2-4所⽰。

        原型物体在抽象的⼩游戏中可以直接使⽤,在正式的游戏项⽬中,也可以⽤来给模型占位置,或者⽤来设置触发器范围等。
        在第1章已经⽤过了球体、⽴⽅体等物体,读者可能已经理解了球体的基本功能,那为什么球体具备这些特性呢?下⾯简单分析⼀下它具有的组件。
        除每个物体都具备的Transform组件外,球体还具有⽹格过滤器(Mesh Filter)、⽹格渲染器(Mesh Renderer)和球体碰撞体组件(Sphere Collider)。图2-5所⽰的Mesh Filter被标记为Sphere (Mesh Filter)。这⾥的Mesh(⽹格)存储的是三维模型的三⾓形⽹格数据。

⼩知识
三维模型是由三⾓⾯组成的⽹格
        ⼀个能看到的三维模型是由很多三⾓⾯定义的基本外形,以及⼀个或多个材质定义的表⾯视觉属性组合形成的。其中每个材质⼜可能包含⼀张或多张贴图。虽然也出现过三维模型的其他表⽰⽅法,但⽬前⼏乎所有的三维模型都是⽤三⾓⾯表⽰的,主流硬件设备也是以三⾓⾯作为三维模型的基本要素。
        三⾓⾯⽹格是⽤⼀个个的顶点(⽤三维坐标表⽰),以及它们之间的连线(每三个顶点序号代表⼀个三⾓⾯)表⽰的。这样就组成了⼤量的三⾓形⾯,组合为Mesh(⽹格)。
        另外,所有组件的最下⽅有⼀个颜⾊较浅的区域Default-Material。它不是组件,⽽是⼀个材质,是专门供⽹格渲染器使⽤的。在第1章⾥给物体换颜⾊,其实就是替换了材质。
        有⽹格渲染器才能指定材质,删除⽹格渲染器,材质也会消失。⽹格渲染器将以指定的材质去渲染物体(渲染理解为“绘制”即可),得到特定颜⾊的物体。当然,能改变的不仅是物体的颜⾊,还有贴图、反光度和凹凸感等更多属性。
        3. 灯光
        Unity场景默认具备⼀个名为⽅向光源(Directional Light)的物体,如果没有它,Unity场景将是漆⿊⼀⽚。那么⽅向光源是如何起作⽤的呢?相关的组件如图2-6所⽰。

                                                图2-6 ⽅向光源具有的组件

        从图中可以看到,除Transform组件外只有⼀个Light(光源)组件,其第1个参数Type(光源类型)是Directional(⽅向光),因此才有了⼀个给全场景照明的灯光。绝⼤多数场景都会有⼀个⽅向光源,作为场景照明的基础。Unity还⽀持其他类型的光源,如点光源、探照灯光源等,它们与⽅向光源⼀样都包含光源组件,区别是光源类型、参数不同。
        4. 摄像机
        Unity的场景默认有⼀个摄像机,叫作Main Camera。如果试着删除它,会发现场景窗⼝中并没有什么变化,但是Game窗⼝变成了漆⿊⼀⽚。
        在3D游戏中,⽤户看到的应当只是从场景的某个⾓度看到的⼀部分,这是显然的,⽽且是必要的。⾸先,3D渲染的原理就是从某个⾓度观察场景,渲染出从该⾓度下看到的情景,不指定观察⾓度和范围就⽆法进⾏渲染。其次,从游戏设计⾓度看,如果玩家能在场景中随意浏览,就能看到本不应当看到的东⻄,游戏机制也就乱了套。
        因此这⾥的“摄像机”就充当了玩家的眼睛,其功能是通过Camera(摄像机)组件实现的,如图2-7所⽰。

        在Camera组件中可以调节摄像机的各种参数,⽽这⾥需要关⼼的是摄像机的位置和⾓度。⽆论是电影还是游戏,摄像机的位置和⾓度都是导演或设计师最重视的,⽽位置和⾓度则是通过摄像机的Transform组件来调整的。
        另外,除了视频还有⾳频。Audio Listener(⾳频侦听器)组件也与Camera组件类似,只不过Camera组件决定游戏画⾯,Audio Listener决定游戏⾳频。3D游戏中的声⾳也是有位置、强度、范围变化的,3D游戏也可以模拟出发声的位置,因此Audio Listener的位置会直接影响最终听到的声⾳。如果⾳源位置和Audio Listener位置距离过远,玩家甚⾄会听不到声⾳。另外也有⽆播放位置的⾳效,则⽆论从哪收听都⼀样清晰,如电影旁⽩、界⾯⾳效等。
        总之,要在游戏中看到画⾯,就必须有Camera组件;要想听到声⾳,就必须有Audio Listener组件。默认的摄像机物体就包含了这两者。

2.1.2 变换组件

        在前⽂的知识中讲到,每个物体有且仅有⼀个Transform组件。可⻅与其他组件相⽐,Transform组件显得⾮常特别。下⾯详细介绍它所具有的功能,如图2-8所⽰。

        总体来说,Transform组件掌管着物体的3种空间位置属性:位置、朝向和缩放⽐例。此外,它还有核⼼功能——“⽗⼦关系”。
        1. 位置
        图2-9所⽰的Transform组件的位置(Position)有X、Y、Z这3个值,⽤来表⽰或修改物体在空间中的位置。其中X代表右⽅,Y代表上⽅,Z代表前⽅,读者可以记住这个对应关系,⾮常有⽤。

        这3个值均为浮点数,且符合国际标准单位制,也就是说单位⻓度是1⽶。
        我们既可以在场景窗⼝中⽤位移⼯具修改物体位置,⼜可以直接在Inspector窗⼝中修改数据来指定物体的位置。
2. 朝向
        Transform组件中的Rotation原意为旋转。由于“旋转”这个词容易在“旋转的动作”和“已经旋转到的位置”之间混淆,因此在本书中会使⽤“朝向”⼀词,以表明它是物体⽬前所具有的状态。
        按照三维设计软件的惯例,Unity中朝向也是⽤3个⾓度表⽰,分别是绕x轴、y轴和z轴的旋转⾓度,这种⽤3个⾓度表⽰朝向或旋转的⽅法叫作欧拉⾓(Euler Angle)。例如,要把⼀个⽴⽅体向右旋转45°,只需要将它的朝向的Y值改为45。
        提前说明,⽤欧拉⾓调整场景中的物体,⽅便且直观,但实际在软件内部,使⽤欧拉⾓表⽰物体的朝向有着致命的弊端。虽然Unity在编辑器⾯板上使⽤欧拉⾓表⽰朝向,但在引擎内部是使⽤四元数表⽰朝向和旋转的。此问题会在第4章游戏开发数字基础中进⾏详细介绍。

3. 缩放⽐例
        Transform组件中的Scale代表缩放⽐例。很明显,旋转、位移、缩放都包含X、Y、Z这3个数值,很容易想象到,缩放也是沿x轴、y轴、z轴的伸缩。例如⼀个⽴⽅体,只沿x轴放⼤,就会变成⼀个⻓棒;这时再沿z轴放⼤,会变成⼀个平板;然后通过调整沿y轴缩放的⼤⼩,可以调整平板的厚度。这些操作都⾮常直观。
        物体是按⽐例缩放的,缩放的⼤⼩不能代表物体的⼤⼩。例如,1⽶的棒⼦伸⻓到10倍是10⽶,⽽10⽶的棒⼦伸⻓10倍是100⽶。缩放本⾝没有物理单位(或者说单位是1),物体的最终⻓度是本⾝⻓度乘以缩放⽐例得到的。
        缩放默认使⽤的是物体局部坐标系。例如制作的⻓10⽶,宽、⾼各1⽶的⻓棒,如果随意旋转它,它的外形依然是⻓棒,也就是说同⼀个物体沿x轴、y轴、z轴缩放的⽐例不受旋转的影响。这⼀概念要在理解了世界坐标系和局部坐标系之后才好深⼊讲解。
4. ⽗⼦关系
        ⼀个场景中可以有很多物体,⽽这些物体并不是随意散布在场景⾥的,⽽是有“⽗⼦关系”的,如图2-10所⽰。

        “⽗⼦关系”让多个物体形成嵌套的、树形的结构。很多游戏开发技术,如⾻骼动画、指定旋转的锚点、统⼀物体⽣命期等问题都可以⽤“⽗⼦关系”表⽰或解决。由于“⽗⼦关系”很重要,因此下⼀⼩节将专门探讨“⽗⼦关系”。

2.1.3 “⽗⼦关系”详解

        前⽂提到,Unity有着简洁的架构,对于笔者来说,最重要的概念只有物体、组件和“⽗⼦关系”。但是简洁性不能以牺牲功能为代价,在实际游戏开发中,有各种各样的实际需求。对于这些实际需求,引擎⼀定要提供可⾏的解决⽅案,否则引擎就是有缺陷的。接下来的内容可以说
明,只要⽤好“⽗⼦关系”这⼀特性,就能很好地解决这些实际问题,⽽不需要引⼊其他概念。
1. 使⽤“⽗⼦关系”复⽤零件
        在某些游戏引擎中,可以重复使⽤的零件也被称为“组件”。例如,制作出的⼀个灯泡既可以⽤在吊灯上,也可以⽤在台灯上。但是这样会带来⼀个概念上的⼩问题,因为⼀旦组件可以单独存在,那么概念就变得复杂了——⼜多出来⼀种可以单独存在的“组件”,甚⾄它还可以有⾃
⾝的位置,如灯泡在吊灯⾥的位置是可以调节的。                                                                                 ⽽Unity严格规定了物体和组件分别能做什么、不能做什么。例如,组件必须挂载于物体之上,不能单独存在;组件本⾝不具有位置参数;由于组件与物体是⼀体的,因此脚本中引⽤组件实际上是引⽤了物体。这样的规定让组件⽆法独⽴存在,从表⾯上看限制了实现时的灵活性。
        可以重复使⽤的灯泡显然是必要的,在很多游戏中都可以找到重复使⽤物体的例⼦。对复⽤零件的问题,Unity的解决⽅案是使⽤⼦物体。简单来说,只要把灯泡做成⼀个单独的物体,然后把它作为吊灯或台灯的⼦物体,这样灯泡就成了吊灯或台灯的⼀部分,问题就解决了。由于
⼦物体可以任意移动位置,因此仍然可以把它放在吊灯的任意位置。
        具体来说,⼀个灯泡可以由3D模型、点光源组成,也可以再给它加⼀些⼦物体装饰。⽆论灯泡做得多么花哨,但作为⼀个整体它⽤起来依然很⽅便,如图2-11所⽰。

2. “⽗⼦关系”与局部坐标系
        每个场景整体都有⼀个⼤的坐标系,称为世界坐标系。世界坐标系也有x轴、y轴和z轴,在Unity中分别规定为右⽅、上⽅、前⽅。⽽在很多情况下,只有世界坐标系是不够的。例如,在移动台灯的时候,希望灯泡能⼀起移动;旋转台灯的时候,希望灯泡能⼀起旋转;放⼤台灯的时候,希望灯泡能⼀起放⼤。在这⾥,台灯就构成了⼀个局部的坐标系,它内部物体的位移、旋转、缩放都要受到局部坐标系的影响,如图2-12所⽰。

        简单来说,局部坐标系就是⽗物体⾃⾝的坐标系,⽗物体的位置就是局部坐标系的原点,⽗物体的右⽅、上⽅、前⽅分别就是局部坐标系的x轴、y轴和z轴。⼀旦缩放⽗物体,所有的⼦物体也会缩放。举个实际例⼦,局部坐标系有⼀个⾮常经典的应⽤——实现房门的转动。
        如果直接旋转⼀个门,⼀般门会沿着模型的中线旋转。如果要规定门的转轴的位置,就可以给门做⼀个空的⽗物体,将⽗物体定位在门的转轴位置。这时旋转⽗物体,门就会绕⽗物体转动了,如图2-13所⽰。

3. ⽤“⽗⼦关系”表⽰⾓⾊的⾻骼
        ⾻骼动画是现代3D游戏不可或缺的基本功能。简单来说,3D⾓⾊不仅有⼀个模型外观,该模型内部还有⼀个虚拟的⾻骼,这个⾻骼的运动会拉扯模型的表⾯材质跟着运动,这样⼀来只需要移动⾻骼,就可以让模型做出各种动作了。
        如果将⼈类的⾻骼结构看作⼀种连接结构,那么从腰部往下看,有臀、左⼤腿、右⼤腿等,腰部以上则有躯⼲、左右胳膊、头部等,每⼀个可旋转的部分都有对应的关节连接。肩膀的旋转会带动整条胳膊的旋转,肘的旋转会带动⼩臂和⼿的旋转,再结合前⽂提到的局部坐标系思考,这些⾻骼关系正好可以⽤“⽗⼦关系”表⽰,如图2-14所⽰。
        事实也确实如此,Unity直接⽤“⽗⼦关系”表⽰模型⾻骼,再在模型表⾯蒙上⼀层可伸缩的⽹格,就像⽪肤⼀样(⽪肤⼀样的⽹格组件叫作Skinned Mesh),这样就⽤“⽗⼦关系”解决了⾻骼动画的问题。



 ⼩知识
Skinned Mesh Renderer组件

在Unity中看不到单独的Skinned Mesh组件。带⾻骼的三维模型使
⽤Skinned Mesh Renderer统⼀管理⽹格和材质,⽽不像⼀般的模型⽤
Mesh Filter和Mesh Renderer分别表⽰。
4. 利⽤⽗物体统⼀管理⼤量物体
        某些游戏会⼤量⽣成同类物体,如射击游戏中的⼦弹物体、塔防游戏中的⼤量敌⼈等。很多时候需要统⼀管理这些⼦弹,如⼀起隐藏、⼀起销毁等。如果它们散布在整个场景中,要同时对它们进⾏操作就会很⿇烦。这时可以创建⼀个空物体作为⽗物体,让这些⼤量的同类对象都成为⽗物体的⼦物体。那么只要隐藏⽗物体,它们就会⼀起隐藏;只要销毁⽗物体,它们就会⼀起销毁。也就是说,结合⽗物体可以轻松设计出有效的物体管理器。
注意
慎重使⽤⾮等⽐例缩放

        ⽗物体的缩放会直接引起⼦物体的缩放,可以认为⽗物体缩放后,形成了⼀个按⽐例拉伸或收缩的局部坐标系。其中,如果沿x轴、y轴和z轴缩放的⽐例相同,就叫作等⽐缩放,如果沿3个轴缩放的⽐例不相等,就叫作⾮等⽐缩放。
        等⽐缩放和⾮等⽐缩放实际差异很⼤。读者可以想象⼀下,当存在多级嵌套的“⽗⼦关系”,每⼀级物体都具有不同的缩放以及不同的旋转⾓度时,那么最后⼀级⼦物体的局部坐标系将会多么复杂和难以理解。
        Unity官⽅⽂档指出:如果在⽗物体上使⽤了⾮等⽐缩放,那么在某些特殊情况下有可能导致⼦物体的位置、朝向计算错误,因此建议在⽗物体上尽可能避免⾮等⽐缩放。图2-15展⽰了⽴⽅体在⽗物体为⾮等⽐缩放时的⼀种情形。

                                图2-15 在⽗物体上使⽤⾮等⽐缩放的情形

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/105984?site
推荐阅读
相关标签
  

闽ICP备14008679号