Unity Basics

关于Unity

Unity可用于开发单人游戏,也能用于开发多人游戏。
Unity内置RakNet(一个网络游戏开发包),适用于快速开发多人游戏。
对于现在流行的"弱联网"非实时互动游戏,Unity提供了HTTP网络通信功能,可以方便地与PHP或.NET服务器实现网络通信。
对于大型的网络游戏,开发者可以使用C#编写基于.NET的Socket客户端程序与使用C++、C#或Java 开发的服务器端实现网络通信。

Unity优点

  1. 提供了极度高效的可视化工作流,并在可视化编辑器中创建所有东西前提下,还包括一系列链接到可运行项目中的自定义代码。
  2. 多维度的跨平台支持,包括部署目标方面跨平台(PC、Web、移动设备、游戏主机)和开发工具跨平台(Windows、Mac OS)。

Unity缺点

  1. 可高效地组合可视化编辑器和复杂的代码,但是会产生复杂度,即在复杂的场景中很难确认哪个物体被附加了指定组件。虽然有搜索功能,但该功能还很粗糙。
  2. 不支持链接额外的代码库,很多现有的库需要手动复制到每个需要的项目,而不是引用一个中心共享的位置。这使得在多个项目中共享类库变得笨拙。

Unity脚本及其声明周期

Unity中的脚本是指继承MonoBehaviour脚本的组件。继承了MonoBehaviour的派生类不能使用构造函数初始化。
MonoBehaviour是脚本组件的基类,其定义了一系列看不见的基础工作,实现了组件是如何被附加到游戏对象上,我们可以重写其继承提供的一系列的方法。
声明周期:Awake——>OnEnable——>Start——>FixedUpdate——>Update——>LateUpdate——>OnGUI——>OnDisable——>OnDestroy。

  1. Awake:
    生命周期的开始,在整个生命周期仅被调用一次,用于在游戏开始之前对对象初始化。各个游戏物体上的Awake以随机的顺序被调用。因此,你应该用Awake来设置脚本间的引用,并用Start来传递信,但它不能用来执行协同程序。
  2. OnEnable:
    当对象组间可用,即对象是正常执行顺序,将执行一次。
  3. Start:
    仅在Update函数第一次被调用前调用。Start在behaviour的生命周期中只被调用一次。它和Awake的不同是Start只在对象被激活,脚本实例被启用时调用(通常是带有MonoBehaviour对象的关卡被加载)。你可以按需调整延迟初始化代码。Awake总是在Start之前执行,这允许你协调初始化顺序。在所有脚本实例中,Start函数总是在Awake函数之后调用。
  4. FixedUpdate:
    固定帧更新,每固定帧绘制时执行一次,即物理更新。和Update不同的是FixedUpdate是渲染帧执行,如果你的渲染效率低下的时候FixedUpdate调用次数就会跟着下降。FixedUpdate比较适用于物理引擎的计算,因为是跟每帧渲染有关。而Update就比较适合做控制。在Unity导航菜单栏中,点击“Edit”–>“Project Setting”–>“Time”菜单项后,右侧的Inspector视图将弹出时间管理器,其中“Fixed Timestep”选项用于设置FixedUpdate()的更新频率,更新频率默认为0.02s。
  5. Update:
    正常帧更新,用于更新逻辑。每一帧都执行,当处理Rigidbody时,需要用FixedUpdate代替Update,因为刚体具有物理属性。例如,给刚体加一个作用力时,你必须把作用力应用在FixedUpdate里的固定帧,而不是Update中的帧(两者帧长不同),Update比较适合做控制。
  6. LateUpdate:
    在每帧中所有的Update函数调用完毕后被调用,和Fixedupdate一样都是固定帧绘制时被调用执行,这可用于调整脚本执行顺序,比较适合用于命令脚本的执行。例如当物体在Update里移动时,跟随物体的相机在LateUpdate里实现。官网上例子是摄像机的跟随,都是在所有update操作完才跟进摄像机,不然就有可能出现摄像机已经推进了,但是视角里还未有角色的空帧出现。
  7. OnGUI:
    在渲染和处理GUI事件时调用。比如:你画一个button或label时常常会调用。这意味着OnGUI也是每渲染帧执行一次,当然,如果使用的是NGUI,这个阶段可以不用考虑。
  8. Reset:
    在用户点击检视面板的Reset按钮或者首次添加该组件时被调用。此函数只在编辑模式下才被调用。Reset最常用于在检视面板中给定的默认值。
  9. OnDisable:
    当物体要被销毁时OnDisable将被调用,并且可用于任意清理代码。脚本被卸载时,OnDisable将被调用。注意:OnDisable不能用于协同程序。
  10. OnDestroy:
    当MonoBehaviour被销毁时,这个函数被调用。OnDestroy只会在预先已经被激活的游戏物体上被调用。

Unity基础常识

Unity的脚本语言基于Mono的.Net平台上运行,可以使用.NET库,这也为XML、数据库、正则表达式等问题提供了很好的解决方案。

Unity常用C#和JavaScript。
使用C#优于JavaScript是因为强类型语言。动态类型更适合开发,特别是Web开发,但针对特定的游戏平台(如IOS)通常使用静态类型会更适合。
虽然Unity增加了指令#pragma struct强制让JS使用静态类型,但破坏了JS工作的根本原理。

网格对象: 指3D空间中的可视对象。3D中的可视对象是由很多连接的线和图形构成的,因此世界都是由网格构成的。

对象:
摄像机,光源,游戏对象(包括物体模型,地图场景,UI界面)。
对象一般有玩家、敌人、环境、摄像机等非实体虚拟父类,但子类常为游戏内的实体;资源一般包含对象、材质、场景、声音、预设、贴图、脚本、动作等子文件夹。
对象的行为主要依赖于物理效果和骨骼动画。

对象与资源的区别与联系:
对象出现在游戏的场景中,是资源整合的具体表现;
资源可以被多个对象使用,有些可作为模板被实例化成游戏中具体的对象。

四元数:四元数用于表示旋转,相对欧拉角的优点有:
1)能进行增量旋转
2)避免万向锁
3)给定方位的表达方式有两种,互为负(欧拉角有无数种表达方式)

向量的点乘、叉乘、归一化:
1)点乘描述了两个向量的相似程度,结果越大两向量越相似,主要用来计算两个向量之间的夹角,以及在某一方向上的投影
2)叉乘得到的向量垂直于原来的两个向量
3)归一化即标准化向量,用在只关心方向,不关心大小的时候

协同程序:
协同程序又简称协程,即在主线程运行的同时开启另一段逻辑来协助当前程序的执行。主程序可以开启多个协程来协助自身的工作。
StartCoroutine是开启协程,StopCoroutine是关闭协程。
协程的返回值是IEnumerator类型,而在协程内部必须有yield return **,IEnumerator用来记录执到yield return的位置。
每次执行协程时均是从当前位置向后执行,而不是从协程的最开始位置执行,除非只有一个yield return。

Unity的协程和C#的线程的区别:
协程在一个时候只能执行一个,并且这个正在运行的协同程序只在必要时才被挂起,而线程一个时候可以并发执行多个,一个线程可以包含多个协程,其相同点是线程和协程一样共享堆,而不共享栈。
协程可以通过某些条件暂时挂起不继续执行,等带达到条件要求再继续执行,即在StartCoroutine函数体进行处理时,利用yield语句等待执行结果,这期间不影响主程序的继续执行,即协同工作。
除主线程之外的线程无法访问Unity3D的对象、组件、方法。虽然Unity没有多线程的概念,但是提供了StartCoroutine(协同程序)和LoadLevelAsync(异步加载关卡)后台加载场景的方法。
注意:C#中有lock这个关键字,以确保只有一个线程可以在特定时间内访问特定的对象。

prefabs类:
预设体的概念: 组件的集合体,实际上不包含任何模型、贴图等素材,只是一个素材的引用集合,预制物体可以实例化成游戏对象,但依赖的素材被删除,就失去了作用。
创建预设体的作用: 可以重复的创建具有相同结构的游戏对象,适合频繁生成的对象。

PlayerPrefs类:
是一个用于保存和读取数据的类,主要用于需要经常修改属性的物体,其用来操作的数据类型为int, float, string。
比如,处理整型的函数为PlayerPrefs.SetInt()、PlayerPrefs.GetInt();

自定义类型的存储:
通常我们自定义的文件类型可以通过textasset和scriptable object,区别在于前者是一个字节或文本流,后者要对应于程序中一个已定义的类型,textasset还通常用于资源的加密。

对象池:
对象池就存放需要被反复调用资源的一个空间,当一个对象需要大量生成,如果每次都销毁再创建会很费时间,通过对象池把暂时不用的对象放到一个池中,当下次要重新生成对象的时候先去池中查找是否有可用的对象,如果有的话就直接拿出来使用,不需要再创建,如果池中没有可用的对象,才需要重新创建,是一种利用空间换时间来达到游戏的高速运行效果的方法。

网络中角色操作的同步,场景中NPC的同步:
角色操作同步可以考虑帧同步的方案,大部分游戏逻辑都在客户端实现,服务器只负责广播和验证操作。
NPC同步可以考虑状态同步,只同步npc的状态行为,如AI逻辑、技能逻辑和战斗计算都由服务器计算,然后将运算结果同步给客户端,客户端接受传过来的变化后的数据状态然后更新本地的动作状态即可。

在场景中放置多个Camera并同时处于活动状态会发生什么:
游戏界面可以看到很多摄像机的混合,即实际看到的画面由多个camera的画面组成,由depth、Clear Flag、Culling Mask都会影响最终合成效果。

游戏场景中可能同时存在的Camera:
主相机:用透视摄像机渲染主场景。
UI相机:用正交相机渲染UI。
深度相机:根据需求设置的,可以通过设置深度保证某些对象一直在最前面显示。

Camera的正交视图与透视视图:
正交视图Orthographic (无消失点投影) :正交视图无法看到一个物体是远离自己还是正在我们面前,因为它不会根据距离收缩。
透视视图Perspective (有消失点投影) :透视视图和我们从眼睛看到的视图是一样的。例如,一个高个子的人站在你面前,他看上去是很高的。

Unity基础实现

简述Lua和C#交互的解决方案:
通过LuaInterface和Luanet两个dll,LuaState中包含DoFile和DoString方法。

组件处理命令:
获取:GetComponent 
增加:AddComponent  
删除:Destroy

对象位置设置:
obj.transform.position = new vector3(0,0,0);
obj.transform.localPosition = new vector3(0,0,0);

代码热更新:
通过C#与Lua的互相调用,Lua是一种小巧的语言,可以在程序运行时编译和执行,就所我们通过lua来实现C#的热更。

粒子系统:
粒子系统是密集的粒子互相作用表现效果,可用于制作特效,例如火焰、烟花、爆炸、技能、碰撞等。

Unity提供的几种光源:
平行光:Directional Light;
聚光灯:Spot Light;
点光源:Point Light;
区域光源:Area Light(只用于烘培);

Atlas:
每个材质和纹理的渲染都会产生DrawCall,把所有密切相关图片做成一张大图,从而减少DrawCall。但是要注意图集粒度,选择什么图片和多少图片取合成一张图集,对内存效率有很大影响等问题,如果是不可能同时出现的东西放在同一个图集,会增大内存占用。

LightMap:
LightMap称为光照帖图,即把灯光和物体设置成静态,然后对灯光进行烘焙,此时在物体表面形成一张有灯光效果的帖图,此时把场景中的灯光删除掉,物体表面依然有灯光的效果,而这种效果是帖图造成的;光照帖图的优点是避免动态光照在场景中的实时渲染,减少Drawcall。

Unity的几种施加力的方式:
rigidbody.AddForce/AddForceAtPosition,都是rigidbody的成员函数。

链条关节:
Hinge Joint,可以模拟两个物体间用一根链条连接在一起的情况,能保持两个物体在一个固定距离内部相互移动而不产生作用力,在达到固定距离后就会产生拉力。

CharacterController和Rigidbody的区别:
Rigidbody具有完全真实物理的特性,而CharacterController可以说是受限的Rigidbody,具有一定的物理效果但不是完全真实的。

碰撞器和触发器的区别:
碰撞器是触发器的载体,而触发器只是碰撞器身上的一个属性。
collider碰撞器会有碰撞的效果,当Is Trigger=false时,碰撞器根据物理引擎引发碰撞,产生碰撞的效果,可以调用OnCollisionEnter/Stay/Exit函数。
trigger触发器没有碰撞效果,当Is Trigger=true时,碰撞器被物理引擎所忽略,没有碰撞效果,可以调用OnTriggerEnter/Stay/Exit函数。
如果既要检测到物体的接触又不想让碰撞检测影响物体移动或要检测一个物件是否经过空间中的某个区域这时就可以用到触发器。

物体发生碰撞的必要条件:
物体A必须带有(collider+rigidbody)或者CharacterController,另一个物体也必须至少带有collider。
物体发生碰撞时,有几个阶段,分别对应的函数:
三个阶段,OnCollisionEnter/Stay/Exit函数。

NGUI:
NGUI能在不同分辨率下保持UI的一致性,即屏幕分辨率的自适应性,原理就是计算出屏幕的宽高比跟原来的预设的屏幕分辨率求出一个对比值,然后修改摄像机的size。
NGUI中Button接受用户点击并调用函数的方法是OnClick()。主要是在UICamera脚本中用射线判断点击的物体,并通过SendMessage调用OnClick()、OnPress()等函数,可以说NGUI的按钮是通过发消息这个方式调用的。

MeshRender(网格渲染)和SkinnedMeshRender(蒙皮网格渲染)的关系与不同: 
Mesh就是指模型的网格,MeshFilter一般是用于获得模型网格的组件,而MeshRender是用于把网格渲染出来的组件。
本质上,两者就不是一个类型的东西。除了两者的原理不同,SkinnedMeshRender还无法做batching,因此会需要更多的DrawCall。同时,骨骼动画的计算更耗时间,但是动作会更精确生动。​
MeshRender中material和sharedmaterial的区别:
修改sharedMaterial将改变所有使用这个材质的物体外观,并且也改变储存在工程里的材质设置。不推荐修改由sharedMaterial返回的材质,如果你想修改渲染器的材质,使用material替代。

LOD及其优缺点:
LOD为Levels of Detail的简称,简单来说即为多细节层次。
LOD技术指根据物体模型的节点在显示环境中所处的位置和重要度,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。
在Unity中,LOD是最常用的游戏优化技术,根据物体在游戏画面中所占视图的百分比来调用不同复杂度的模型。简单而言,就是当一个物体距离摄像机比较远的时候使用低模,反之使用高模。
优点是优化游戏渲染效率,解决运行时流畅问题,缺点是会占用大量内存,是一种空间换时间的方式。

MipMap及其优缺点:
Mipmap技术有点类似于LOD技术,但是不同的是,LOD针对的是模型资源,而Mipmap针对的纹理贴图资源。使用Mipmap后,贴图会根据摄像机距离的远近,选择使用不同精度的贴图。
优点是优化显存带宽,用来减少渲染,根据实际情况选择适合的贴图来渲染,距离摄像机越远,显示的贴图像素越低,缺点是占用内存,因为mipmap会根据摄像机远近不同而生成对应的八个贴图。

OnBecameVisible及OnBecameInvisible的发生时机,以及这一对回调函数的意义:
当物体是否可见切换之时。可以用于只需要在物体可见时才进行的计算。

为什么dynamic font在unicode环境下优于static font:
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。
使用动态字体时,Unity将不会预先生成一个与所有字体的字符纹理。当需要支持亚洲语言或者较大的字体的时候,若使用静态纹理,则字体的纹理将非常大。

FSM有限状态机:
FSM是一种数据结构,它由以下几个部分组成:
1)内在的所有状态(必须是有限个)。
2)输入条件。
3)状态之间起到连接性作用的转换函数。
使用FSM的原因是它编程快速简单,易于调试,性能高,与人类思维相似从而便于梳理,灵活且容易修改。

现在的主流游戏在战斗中的寻路怎么实现?
客户端一般都用navmesh,服务器端是可以导出navmesh的信息后,自行编写算法计算寻路。​

U3D实现2D游戏的方式:
1)利用引擎自带的GUI。
2)把摄像机设为Orthographic(正射),用面片作为2d元素。
3)利用第三方插件:NGUI、2dToolkit。

UI动画实现方案:
1)帧动画方式,即将UI动画每一帧都制作成贴图,然后在UI身上挂载脚本,通过修改UI身上的图片来达到动画效果。
2)使用特定的Shader,这个需要专门Shader的支持。
3)使用UI的Transform动画,那么可以使用插值计算(位置、旋转和缩放),也可以使用常用的插件动画,如Dotween。

游戏动画种类和原理:
主要有关节动画、单一网格模型动画(关键帧动画)、骨骼动画。
关节动画把角色分成若干独立部分,一个部分对应一个网格模型,部分的动画连接成一个整体的动画,使用了这种动画角色会比较灵活。
单一网络模型动画由一个完整的网格模型构成,在动画序列的关键帧里记录各个顶点的原位置及其改变量,然后插值运算实现动画效果,角色动画较真实。
骨骼动画是广泛应用的动画方式,集成了以上两个方式的优点,骨骼按角色特点组成一定的层次结构,由关节相连,可做相对运动,皮肤作为单一网格蒙在骨骼之外,决定角色的外观。皮肤网格每一个顶点都会受到骨骼的影响,从而实现完美的动画。(骨骼动画是由关节动画发展而来的,如今基本都使用骨骼动画来实现角色动画)

Animation(动画)中Curves如何使用:
切换至Curves后,可以添加Property,通过选择不同的属性,在视图中添加Key点修改函数曲线,从而实现物体的各种动画效果、颜色渐变等。

Unity如何安全的在不同工程迁移asset数据:
方法1,可以把assets目录和Library目录一起迁移;
方法2,导出包;
方法3,用unity带的assets server功能;

获取GameObject上的多个Compone的开放属性,并用UGUI形式展示出来:
对于系统组件,由于都继承于Component,可以使用Component[] objs =  gameObject.GetComponents()这种形式获取到对象身上的所有组件。
然后遍历数组,配合switch(类型甄别),之后使用反射获取这个组件中的所有开放属性。
得到后,使用Unity的text展示即可。

Unity动态资源的加载

Unity在打包发布程序时,会自动将场景需要引用到的资源打包到安装包,但不需要引用的资源将不会打包到安装包,编辑器里的Asset文件结构只是编辑器环境下的,在游戏中Unity会重新组织加载资源,这时候就需要动态去加载所需要的资源。
动态load资源有两种途径:
1)通过Resources模块
调用Resources.load函数,直接返回某个类型的Object,前提需要把所有资源放在Resouce命名的文件夹下,Unity不管有没有场景引用,该文件夹下的所有资源都会将其全部打入到安装包中,缺点是不利用分包发布和版本升级。建议这个文件夹下只放Prefab或者一些Object对象,因为Prefab会自动过滤掉对象上不需要的资源。举个例子我把模型文件还有贴图文件都放在了Resources文件夹下,但是我有两张贴图是没有在模型上用的,那么此时这两张没用的贴图也会被打包到发布包中。假如这里我用Prefab,那么Prefab会自动过滤到这两张不被用的贴图,这样发布包就会小一些了。
2)通过Bundle的形式
通过Bundle将资源封装成Assetbundle并放在服务器或本地磁盘中,然后使用WWW模块get下来,再从已被分成多个的bundle中load需要的,适合分包发布和patch,但是在开发过程中,每更新一个资源就打一次bundle是不现实的。
3)通过AssetDatabase.loadasset
这种方式只在开发editor中有效,游戏运行时没有这个函数,通常是在开发调试中使用,在开发中使用AssetDatabase时,需要我们封装一个dynamic resource的loader模块,在不同环境做不同的实现。

Unity动态资源的存放

有时需要存放一些文件在用户磁盘上,需要在初始的安装里放入必要的文件, Unity有一个Streaming Assets的概念,用于提供存储接口的访问。
这时需要在编辑器建立以StreamingAssets命字的文件夹,把需要放在客户磁盘上的动态文件放在这个文件夹里,我们可以通过Application.streamingAssetsPath来获得这个文件夹在磁盘的指定位置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值