惜颜705 发表于 2020-12-11 14:04

游戏引擎 Unity 的入门易精通难体现在哪?为什么?

游戏引擎 Unity 的入门易精通难体现在哪?为什么?

快乐人L 发表于 2020-12-11 14:09

接触Unity一年多,参与过两款上线游戏的开发(iOS/Android)。根据个人感受回答一下。细节中有错误的地方欢迎指正。

个人认为Unity相比于其他引擎易用性较好的原因主要有:

基于组件(Component)的关卡设计更符合直觉
Unity通过将系统或用户的脚本抽象为可重用的组件(Component)并附着在游戏对象上来控制游戏的行为。相较于传统的基于脚本的开发方式,关卡设计师可以更灵活、更快速地搭建界面和关卡,有一种“搭积木”的感觉。虽然这种设计牺牲了一部分扩展性(例如难以实现嵌套Prefab),但对初学者来讲是非常友好的。

虚幻引擎4.7版本的更新也效仿Unity,向组件化的方向靠拢,使关卡结构更容易理解和维护。

使用Mono作为脚本运行的平台
C#/Mono相比C++和其他脚本语言,有更好的稳定性和抽象能力,有容易使用的.NET框架和易于移植的各类开源库,相对完善的语言服务(如GC和反射)也使开发复杂逻辑容易许多。虽然降低了门槛的同时也让低质量的代码更容易产生,但不管对于初学者还是老鸟来说,我觉得都是利大于弊的。

引擎本身的功能相对简单 + 丰富的Asset Store插件
和虚幻等超级引擎相比,Unity提供的功能算是非常基础的,组件数量和各组件可配置的内容都不多,所以在学习的时候更容易产生比较直观的感受,不至于迷失在细节当中。另一方面,Asset Store模式的成功造就了大量功能强大的第三方插件,填补了Unity开发中的各种空白,进一步降低了开发门槛。

而难于精通方面,我觉得主要原因在于:

对综合能力要求高
首先,不仅对Unity,对任何游戏引擎或者对游戏开发本身来说,想要精通都是很难的一件事。因为游戏客户端开发本身是一项综合性非常强的活动,整合的技术非常多,例如:
建模关卡制作脚本逻辑网络通信平台特性集成动画制作特效制作工作流集成调试和优化
而对于将这些技术粘合在一起的Unity工程师来说,虽然不需要精通方方面面,但将团队成员的工作高效率高质量地结合在一起,也是非常考验其能力的,只有长期地,全方面地参与整个开发过程并了解团队成员的工作方式,才能逐渐成长为一名优秀的Unity工程师。

举例来说,Unity工程师需要:
与设计和美术团队沟通,评估设计对于游戏性能的影响,实现原型,进行各种性能测试。与服务器工程师沟通,确定互相之间的接口和协议内容的细节。Unity本身没有一个Gameplay Framework,场景管理、游戏数据管理等相对底层的框架都需要Unity工程师来搭建,如何减少团队中其他成员的错误实践,是主程序的责任,也很考验其架构能力和对Unity Runtime的理解。一些特效需要自己编写Shader才能实现。动画师和美术团队产生的资产,根据项目的需要常常要自己写Pipeline来导入和进行优化处理。一部分还需要以合适的方式打包,供客户端增量下载,需要对Unity的资源管线有较好的理解才可以。而且看似简单的决定之中,常常蕴含了很多性能上的考量。根据关卡设计师的需要,制作编辑器扩展工具,提高其工作效率。这方面的文档稀少,同时也需要对Unity独特的序列化机制有比较深的理解才行。想利用iOS和Android以及各种平台特有的功能时,需要针对特定的平台编写一些Native插件,如本地Push通知,自定义系统键盘,系统弹窗等等。要懂一些iOS开发和Android开发的知识才能驾驭。
有些做Unity的工程师可能只是搭建关卡,写一些控制脚本,而优秀的Unity工程师的价值往往在于其能够承担更多的团队职责。所以说想要精通这些,真的需要付出很多的时间和努力才行。

难在细节

任何事想精通,细节都是非常重要的,比如:
内存管理
避免和排查脚本中的内存泄漏。例如没有清空的委托和静态闭包造成的引用。优化GC,了解Mono和.NET GC算法的差别。比如Unity采用的是较老版本的Boehm GC,不分世代,GC分页为1KB,内存碎片无法合并,单线程,缺少LOH,托管堆一旦扩大就很难向系统返还内存。如果按照.NET的思路优化GC,有时候并没有理想的效果。而Unity最近引入的IL2CPP运行时,采用了新版本的Boehm GC,算法又有所改变,优化策略也应适当调整。了解Unity的内存模型,哪些资源分配在Native堆上,哪些分配在Mono的托管堆上。Native上不同种类的资源分别用哪些方法能够释放干净。Native堆上的引用计数是如何工作的。如何减轻Unity自动释放场景时的压力。AssetBundle的内存结构是怎么样的,各个部分如何不依赖GC而精准地释放。暂时隐藏起来的图片或物体如何暂时性地释放,在显示时又如何重新加载回内存。了解哪些API和操作会分配内存,什么时候使用值类型更好或更不好。这都需要对C#有很深入的理解。
网络和下载
AssetBundle如何打包,什么样的图片用什么粒度打包效率最高。如果使用Json反序列化数据,怎么才能避免内存碎片降低整个App的性能。不断更新的素材需要增量下载,Unity内置的下载方式瓶颈在哪,怎么自己实现一个比Unity内置API更高效的下载机制和更精准的缓存控制机制。
脚本执行
能够将较复杂的操作(如反序列化数据,较重的IO操作)放在后台线程执行,再调度回主线程更新游戏界面,以避免UI卡顿。Unity的线程优先级又是怎样的?能否理解Unity Coroutine的迭代器本质,怎样对Coroutine中的异常进行处理,如何使Coroutine具有返回值,Coroutine启动时将分配多少内存,为什么复杂的Coroutine会使用更多的内存,如何将多个Coroutine合并为一个从而消除内存分配。在与Native插件(如iOS插件)交互时,如何让C#与Objective C或Java共享内存,从而减少大型数据封送造成的CPU负担。iOS平台上AOT异常的根本原因是什么,有哪几种。值类型和泛型的组合更容易引发AOT异常的原因是什么,如何绕过。怎么安全的使用Linq,使用C#标准事件为什么会触发AOT异常,怎样避免。Mono AOT的trampoline又是什么,哪种风格的代码更容易耗尽trampoline并引发AOT异常。Unity的C#编译器有哪些弱点,哪些代码通过Visual Studio编译成DLL再放到Unity中可以提高执行效率和内存使用效率。场景加载缓慢等Unity内部表现出来的问题如何从自身脚本下手进行优化。或者说不同性能问题在自己代码中的Critical Path都是哪些部分。
渲染
Draw Call是什么。不同AssetBundle中的小图片如何Batch到一个Draw Call中。移动平台常用的Tile-based GPU有哪些弱点,如何避免。Retina等高清屏幕上制作2D游戏时,如何动态为图片生成最小的Mesh网格以节约fill rate。
团队协作
自己编写Gameplay框架的情况下,如何控制队友代码中的内存泄漏。使用版本管理系统时会产生哪些难以解决的冲突,如何建立开发规则。自己开发的框架或工具是否能有效避免队友间发生冲突。如何实现资源管线的自动化。如何将各种奇葩动画编辑器的输出转换成Unity标准的动画资源。制作编辑器扩展时,是否能正确序列化复杂的数据结构。能否让自己的工具和脚本也实现所见即所得,让队友更快的搭建场景。是否会使用Gizmo和Handle来扩展场景编辑器。当不得不修改MonoBehaviour的定义时,怎样让已经上线的老版本中的数据正确地反序列化到新版本。要有能力编写模块划分明确,依赖关系合理清晰的可重用代码和组件,作为公司的资产加速新项目的开发。

这些都是Unity开发中会不断面对的问题,如果不能从始至终中控制住这些细节,积累起来往往会使团队效率底下,难以产出高质量的应用。

所以我觉得Unity难以精通之处就在于对细节知识的把握,以及在整合团队价值的过程中如何做到扬长避短。Unity开发团队中常常不是所有人都会这个引擎有很深刻的认识,大家术业有专攻,有人搭场景,有人做后端,对自己不熟悉的领域,难免有错误的认知和实践。我们尚可以通过时间和努力精通细节,然而到头来真正缺少的,其实是团队成员间的信任所带来的沟通成本的降低。

【更新】
回答一些朋友的疑问。

关于资料来源
细节问题很难有系统的资料来源,而像AssetBundle Dynamic Batch这种几乎找不到答案的问题只能自己慢慢摸索。在此列举几个最主要的知识来源。
官方手册。例如搜索Unity Optimization可以找到官方在GPU,CPU和Mobile方面的几篇优化手册。官方资料常常包含最核心也最容易被忽略的原则,深入理解往往会有新的收获。官方博客。博客上不时会有一些技术类文章,如关于IL2CPP和序列化机制的知识几乎只能看那几篇博客。TechnologyUnite的视频和Slide。对某些领域有比较深入的探讨,特别在内存管理,AssetBundle和代码组织方面。YouTube和SlideShare上搜Unite或Unity能找到。值得注意的是Unite的分会场,如日本和韩国,有时会有一些更加深入的分析。如 slideshare.net 的页面Unity的Mono fork:Unity-Technologies/mono · GitHub ,关于GC和AOT方面是第一手的资料。比如gc的配置,Enumerable类的实现如何导致Linq容易触发AOT异常,以及泛型CompareExchange中存在的JIT Hack导致C#事件和一些线程同步操作触发AOT异常等等。另外GC方面也可以参考ivmai/bdwgc · GitHub ,有详细的机制解释。UI的源代码:Unity-Technologies / UI ,修改优化后可以直接集成到游戏中,很方便。随Unity安装的PDB调试文件。Unity的安装目录下其实是有Editor和Player当中所有C++源码的PDB文件的,而且居然都是private PDB。需要探查Unity内部数据结构和过程实现的时候,通过WinDbg配合这些PDB文件调试Unity进程可以获得很多最底层的信息。当然,如果公司买了Unity的源码就不必这样麻烦了。

关于优化策略
不只是Unity,优化程序最重要的原则就是先测量。而且在没有丰富经验和自信的情况下,不要自己写测量代码,而要依赖Unity自己Profiler和Profiler API。这里只说一些Unity特有的内容。

使用Profiler时,切忌猜测。一定要弄清各种数据的精确涵义,如Self %,Self ms,GC Alloc等,如果弄不清楚,优化常常是南辕北辙。另外诊断CPU Spike时,一定要打开Deep Profile,否则只能看到误导性很强的表面数据。找到真正的Hot Line才能着手优化改善性能。

当Hot Line在自己的代码中时,可以尝试将CPU密集的操作分派到后台线程,然后在需要与Unity API交互时调度回来。粒度较好的操作可以尝试用Coroutine分派到其他帧分别执行。大量GC Alloc造成的Spike需要重新设计内存分配策略,小对象(目前版本是小于1KB)较多的时候也可以尝试预先扩大托管堆(如分配很多小于1KB的缓冲区,然后再释放),这样可以加速后续内存分配。因为Boehm GC的堆扩展策略是时间线性而非空间线性的,所以每次扩大后的容量都是翻倍的,需要注意。

而当Hot Line在Unity API中时,常常是自己的错误实践造成的,需要重新审视设计。一方面要减少昂贵API的调用次数,一方面要降低Unity内部处理数据的规模。例如场景加载缓慢时,可能需要简化场景本身,然后在场景启动后再手动、增量地加载场景中的其他内容。

另外要了解一些底层知识。例如App启动时性能较差的原因,在非AOT平台上可能是因为大量的JIT编译造成的,而在AOT平台上则可能是因为初始化代码过于复杂导致CPU缓存命中率很低,和操作系统频繁地Page Fault。这也是为什么启动代码一定要精简,并且要尽量实现批处理。

GPU方面,如果没有复杂的特效,瓶颈常常在Draw Call和Fill Rate上。Draw Call需要Batch,能共享的材质一定要共享。Fill Rate的问题通常在高分辨率的2D游戏中比较明显,Profiler中的Transparency渲染占比很大时就应该着手优化。GPU优化策略上Unity相比其他引擎并没有很多特异的方面,准确测量的基础上通常能找到比较通用的解决方法。

123456835 发表于 2020-12-11 14:19

Unity入门易:


1、渲染对象上挂一个脚本组件就可以驱动该对象的逻辑,基于MonoBehaviour的脚本一上来就把初始化(Awake、Start),更新(Update、FixedUpdate)的接口留好了,初学者完全不用考虑程序框架一类的问题,直接填空就行。很像做早期flash游戏的感觉。

2、编辑器非常强大,所见即所得的编辑方式,可以随时暂停、单帧执行游戏逻辑,提供场景和游戏多个窗口实时调试,观察效果。脚本组件面板上可以实时看到所有变量的当前值,这对于调试游戏逻辑非常方便。

3、3D引擎功能很完善,与我们自研了5,6年的3D引擎相比,还是远远甩我们一大截,除了支持前向和后向的多种渲染管线,各种后渲染效果,还内置基于Beast的LightMap烘焙、基于Umbra的遮挡剔除这些商业中间件,要知道单采购这些中间件就得好几十万RMB,因此就算是个3D新手,要做个炫酷的3D效果也不是很难。虽然在渲染效果上可能比不上Unreal,Cry,但是U3D这玩意在中国,就算是免费的了,再说用U3D基本也是要发移动平台,目前的3D效果已经很够用。

4、游戏其他方面的组件也很丰富,有基于PhyX的物理系统,你要做点什么疯狂小鸟、割绳子之类的游戏,真是很容易。另外还有基于NavMesh的导航系统、音乐音效系统(做音效的是FMOD的前开发者)等。

5、良好的开发者社区生态系统。Unity最NB的就是建了个Asset Store,全世界的UNITY开发者在这里卖自己做的各种代码、组件、美术资源,分享经验。因此,学习成本大大降低。

6、一键发布到各种平台,包括IOS、Andriod、WP、网页、Windows、Mac等,如果不是要接入其他平台相关的库(如内购等),几乎完全不用学习平台相关的编程知识(Object-C, Java等)

7、作为证明Unity入门易的一个例子,本人在刚开始学Unity时,用了一个半月的业余时间,做了个推币小游戏,放在AppStore上,一个月基本上能收个几千块钱。给个链接吧,不算广告啊,只是说明下1个半月业余时间用Unity能做个什么样的出来:iTunes 的 App Store 中的“小丑马戏团”
--------------------------------------------------------------------------------------------------------------------------------

Unity精通难:

1、基于MonoBehaviour的脚本,用得太顺手会有有很大的架构风险,你会情不自禁的A组件引用B组件,B组件又引用A组件.....项目一大写成一团乱麻。当然用其他引擎,其他语言也有这问题,但UNITY的一些特性必须用MonoBehaviour类来使用,所以要设计一个健壮的MVC架构需要顶住很多诱惑,绕一些弯。

2、基于MonoBehaviour的脚本,组件的初始化顺序无法明确,这个坑也有回答提到了。

3、底层代码不开源,尤其是Asset Store上卖的东西那帮开发者也习惯弄个dll封装起来,因此,一些底层修改需求没法改,有时候很头疼。

4、C#的GC问题,这个不能说是Unity的错,C/C++的开发者会在长期的工作中变得对内存敏感,而C#开发者会弱得多,但游戏恰巧又是个内存敏感的应用程序。因此,开发者需要对C#的内存分配时刻非常敏感,否则就会出现频繁GC导致的顿卡现象。这里尤其要注意在每帧更新时的小内存分配。这方面要善用UNITY自带的内存和性能分析工具。

5、Unity3D毕竟是个3D引擎,3D图形学知识毕竟门槛很高,要深入的做一个项目,需要对3D知识了解得很深,因此,在开发一些大型项目时,新手往往对于3D的各种需求和问题,如果Unity官方没有,或者Asset Store里找不到,就感到棘手。

6、最大的坑!IOS发布时遇到跟AOT编译有关的运行时异常。简单说就是Unity采用mono对C#进行跨平台编译,但在iOS平台中,Mono是以Full AOT模式运行的,无法使用JIT引擎,于是引发了这个异常。所以经常出现的情况是在PC和Andriod上游戏都跑得很好,但在iOS平台会在运行时当掉!具体限制请参照:http://docs.xamarin.com/guides/ios/advanced_topics/limitations/#.NET.c2.a0API.c2.a0Limitations

7、作为证明Unity精通难的例子,我司跨时代炫酷国际大作秒杀Appstore前20的Unity项目,做了大概8个月了吧,估计要出来还得小1年....

--------------------------------------------------------------------------------------------------------------------------
没想到真的已经过了一年了......
我们的项目叫《进化:启示录》,目前在XY越狱平台提供试玩版下载







虫母简单—在线播放—优酷网,视频高清在线观看
http://v.youku.com/v_show/id_XMTI4MjQ3OTY0MA==.html?from=y1.2-1-99.3.5-1.1-1-1-4-0


巨臂丧尸容易—在线播放—优酷网,视频高清在线观看
http://v.youku.com/v_show/id_XMTI4MjQ3OTY1Mg==.html?from=y1.2-1-99.3.4-1.1-1-1-3-0

============================================================================
跟这一年伴随的还有一个叫《坑.xlsx》的文件:


祝各位好运!

ffycxyw2274436 发表于 2020-12-11 14:27

为什么入门简单,看一下官网的文档和视频教程就知道了,看完几段视频和例子就能让初学者做出能玩的东西,其他同类商业引擎都做不到。物体+组件的结构,所见即所得的编辑方式,都把初学者的门槛降到了最低。

再说说难精通的体现。其实任何可以拿来做高大上酷炫屌游戏的商业引擎都是难精通的,这个由引擎的功能深度和泛用性决定。一个集成了无数商业中间件的引擎,包括动画系统、GUI、光照系统、寻路、物理等各个部分,任何一部分单独拿出来都需要用户用心掌握。从这个角度来说Unity和Unreal基本是差不多的。

那么Unity特有的坑有哪些呢?

1. 脚本执行顺序:MonoBehavior是Unity组件系统的核心,离开这个类所有易上手和高效开发的特性都无法施展。但多个组件脚本的执行顺序控制始终是令人头痛的问题。不了解这一点的用户很容易陷入各种隐藏bug的泥沼。

2. 组件式功能结构给每个人更大的控制权力,但无法很好的区分团队不同成员的责任。相比Unreal把编程分为C++,UnrealScript和Kismet三层的做法,Unity让每个开发者都能看到脚本源码并在需要时进行修改。对于独狼开发者来说非常方便,但如果一个游戏逻辑同时可能受到脚本程序、prefab参数配置和场景引用关联三方制约,维护起来就非常的乱,出了问题也很难第一时间分清责任。

3. 内存分配和垃圾回收造成的效率问题初学者很难了解。Unity的一个重要特征就是为了让整个引擎有更广的受众,故意隐藏了很多复杂的使用方法。比如说全自动的垃圾回收,就是为了让不懂内存分配的用户也能无障碍使用。但很快进阶一点的用户就会发现大量运行时垃圾回收造成的拖慢,以至于成为一个入门必经的陷阱。如果能在官方文档里告诉用户哪些操作会分配内存,情况会改善许多。

4. 功能迭代推新迅速,造成大量不成熟不完善的工作流程。这一点很难说是缺点,毕竟能第一时间用到酷炫的新功能对很多用户来说是求之不得的,当然如果你是商业项目的技术负责人就要小心了。一般来说,官方推出的功能至少要在一年后稳定性和适用性才会超过Asset Store上的同类第三方插件。

总的说来,由于优秀的可扩展性,上述这些问题都不致命。总有人在制作优秀的工具来弥补这个引擎的各种不足。而配合其天下无敌的易用性,unity整体的技术方向是非常成功的。

六翼天使494 发表于 2020-12-11 14:32

入门易是因为它的编程脚本话(非常对象化),这个是核心。然后在这个核心之外,价格因素吸引了很多的开发者和公司。加上它的插件化技术形成了很好的生态环境,因此学习资料比其他引擎多了不止很多两个字就能说的清。这些都形成了入门的门槛很低。

精通难是因为3D本身,而不是unity这个工具。

123456848 发表于 2020-12-11 14:33

不打算说【入门易】的体现,没觉得入门易。这里只尝试说几点【精通难(坑)】的体现。

1. 游戏上线后的逻辑更新问题,主要是游戏上线后的持续开发和BUG修复一类的内容。Unity3D在安卓平台上可顺畅的做到逻辑更新,而在IOS平台上因为FULL AOT的限制,只能借助一些第三方的脚本方案来做逻辑更新,而这些第三方的脚本方案用起来限制多,不舒服是一方面,更要命的就是性能差,有无法绕开的性能瓶颈=>【反射】。故目前看来,IOS平台上的逻辑更新解决方案全都是鸡肋,基本不具备实用价值,除非你只打算做斗地主类型的游戏。

2. 底层不开源的问题,这一点前面也都有提到过,一些问题只能靠猜(不停的测试)来解决。

3. 整体包大小的问题,这个似乎也没什么好的办法来解决。这一点从去年上线的一些U3D游戏包的大小上可以看出。

4. 整体性能优化的问题,不易搞,不了解底层,注定无法采用科学的方式来优化性能。 这一点也可以从去年上线的U3D游戏的表现上看出来。

5. 直接的MonoBehaviour脚本式写逻辑,会带来的代码维护和理解上的问题,对开发者的结构设计能力有要求。

David902 发表于 2020-12-11 14:39

入门易:最大限度的不用造轮子
精通难:坑多难调,程序员对程序资源内存等掌控度低的让人发指
(并非不能提高,而是提高方面成本很高,这也是难点啊)

永远就三年疗 发表于 2020-12-11 14:48

如果把跟教程做出个demo叫做入门的话,那确实容易

luly靓 发表于 2020-12-11 14:55

入门明明很难,毕竟官网打不开

我是的十八簿 发表于 2020-12-11 14:56

请问下那个独立开发的游戏 那些模型是你自己做的吗
页: [1] 2
查看完整版本: 游戏引擎 Unity 的入门易精通难体现在哪?为什么?