贵州白癜风微信交流群 http://nvrenjkw.com/nxzx/5604.html5月28日,云畅游戏CEO高云峥和Unity的高级解决方案工程师李中元两位大咖进入了Unity直播间,为大家带来了技术分享。大波粉丝提问之下,问答区共计收到了50多个问题,还挖出了不少制作幕后。
本文精选了本次线上分享会的精彩内容,欢迎大家前往B站观看完整版。
01
基于Unity的改进与定制
实现Console级的动作手游
大家好,我是云畅游戏的CEO高云峥,可以叫我Bobby。今天我演讲的主题是《基于Unity的改进与定制,实现Console级的动作手游》。
首先,我们公司非常荣幸在年开始获得了CAPCOM的授权,开始以《鬼泣》为IP开发手游,就是《鬼泣-巅峰之战》。这款手游我们历经3、4年的时间,与CAPCOM一起打造了一款真正意义上的《鬼泣》空战手游。
我先从整个产品的特性开始。首先大家都知道《鬼泣》这个IP是一款非常经典的主机IP,它从PS一代开始一直延续到PS五代。鬼泣的作品也从一代一直到五代,包括各个特别家庭版,包括DMC,每一代的作品动作片,基本上都是天花板的水平。当我们想要把这款游戏带到手机上来的时候,我们首先要借助一个很好用的工具。我们在评估下来之后发现Unity是一款非常合适的引擎,去帮助我们在手机平台上打造鬼泣。
下面就以几点主要核心的说明,具体阐述Unity的表现能力以及在《鬼泣-巅峰之战》研发中的应用。
01头发的制作
首先我们讲头发。为了表现更柔顺的效果,我们使用了两个pass进行渲染,同时,我们把头发分成了内层、外层和修饰层三个方面。左上角的图在做三个方面的切割。
内层是我们防止穿模的层,帖图几乎是不透明的;头发的外层是我们在做但丁基础的外形,这个主要的目的是角色设计;最后一层是修饰层,主要是为了修饰外形的,主要是为了增加一些细节,我们看到的头发的一些撅起的部分,一些发梢刘海的效果,都是我们修饰的效果,这个是为了让头发显得更加生动,因为贴图发量很少。
02动作系统
我将从三个方面阐释《鬼泣-巅峰之战》如何实现在战斗中的酷炫效果。
首先,流畅舒适的动作感受必不可少。为了达到流畅且合理的动作效果,我们将它的速度设为渐变,同时配合我们的BlendTree设置,使得动作实现流畅的变化。再辅以其他合理的机制,比如在加速跑到待机的过程加入对应的急停动作,使得《鬼泣-巅峰之战》的动作感受达到整体的舒适和统一。
此外,我们还对复杂动作进行了动作分层的处理,为了在动作切换的时候不是太过僵硬。比如但丁开枪行为展现的同时,也会接收玩家发出的移动操作,这种复杂动作我们就要采用更复杂的动作分层相关技术。我们将上半身抬手进行射击的区域,与下半身双腿移动的区域进行分增与融合,结合玩家输入型反馈,最终实现一整套复杂的动作行为,通过分层的方式我们对于复杂的动作进行解构。
最后,在动作切换方面我们也下了大功夫。我们把不同的动作进行划分,可以划分为基础动作和行为动作,基础动作是指人物的待机、移动、转向,行动动作指释放技能、受击、与场景交互。基础动作来说,任何动作的起点和终点都是有待机行为的,这个是有要求的,不能有特别大的动作,移动是要有速度,前面的BlendTree能够生效,他们整体的规格务必要一致,比如说人物先迈左脚还是右脚的问题就必须要纳入考虑了。
而对于行为动作,我们又可以进行二次细分,分为主动行为动作与被动行为动作。顾名思意,主动行为就是由于单位自身上主观意愿进行的动作行为,比如释放技能,被动行为就是由其他单位导致自身进行的动作行为,比如受击。下图为某个切换过程的融合配置:
高云峥老师还分享了《鬼泣-巅峰之战》是如何制作角色表情、角色AI、关卡、后处理优化等内容,尽在B站完整录播中。
02
《鬼泣-巅峰之战》精选问答
本次出镜答疑的,除了云畅游戏CEO高云峥,还有《鬼泣-巅峰之战》的制作人周辉和技术负责人缎君衡。
作为《鬼泣-巅峰之战》的首款手游作品,《鬼泣-巅峰之战》是如何尽量还原主机端游的触感的呢?
周辉:这个问题我们拆开两块来讲。首先说一下原版的操作方式,原版是可以通过手柄或者是键盘来获得按键反馈,这样玩家很清楚知道我的按键是什么,实现了什么操作。然后,我们在《鬼泣-巅峰之战》里开发了这么一个锁定的功能。通过锁定,通过推摇杆前后配合组合按键实现连打的操作。
其实这种手动锁定的方式在手游里用的比较少,我们在最开始制作的时候也是没有制作这个操作方式的。随着开发版本的迭代,我们主动加入了锁定的方式,这也是我们在手机上实现的比较有特色的点。通过不同按键的组合方式,去替代原版的手柄操作,锁定+前后推遥杆的组合按键,在这个上面降低玩家的操作复杂度,同时也保留了原版所有的技能和招式,玩家在手游里也可以做实时连打等操作。
高云峥:我也帮周辉扩展一下,因为《鬼泣》是一个传统的端游作品,端游共有12个按键,所以我们在手游研发时做了大量的工作去简化,这不仅是策划的工作,技术的同学也参与其中,包括适配手柄等,这样大家既可以在手机上体验,也能够连接手柄获得更好的体验。
空中战斗动作设计方面,与地面战斗有什么不同的地方?
周辉:这个问题问得比较专业,对于一个动作游戏来说,在空中和地面其实在动作上是完全两种制作方式。如果说传统我们在地面上打击和打击反馈都是人在地下,我双脚是在地面的,我攻击都会伴随一些角色往前走。所以我前面看到无论是挥刀,还是做一些射击的动作,通过这种方式来体现角色的打击感,这个对打击感的帮助也是很大的。包括受击一方也会给玩家带来一个比较真实的受击反馈。
但如果是在空中,首先角色在空中很难前后的移动,因为你更多还是基于上下的位移,同时我们所谓的怪物在空中的受击也跟地面完全不一样,最重要的因为是我们要在空中还原一个比较真实物理反馈的感受,当我在空中跟怪物发生攻击之后,比如说有击坠,或者有再次击飞,这块就需要我们的物理算法能够达到一个相对真实的反馈体验。然后包括很多怪物从空中到地面,再从地面反弹起来的展现的形式也是需要我们去单独的处理的。
所以,空中的话实际上会比地面在打击反馈上更难以处理,包括在比如说我使用一个连打型的武器,全套在空中跟怪物追加的连打,一套连招打完之后,我对怪物相当于造成一个最终一击的状态的时候,这个时候其实对反馈更加要求难度大,同时要求更加真实。
《鬼泣-巅峰之战》的研发过程当中,Unity的引擎解决了哪些研发中遇到了印象最深的问题?
缎君衡:我认为Unity在我们做《鬼泣-巅峰之战》帮助最大的就是动画状态机。我们之前尝试过自己做一些控制器和编译器,但是效果都不太好。我们在最后切到3D机以后,比较大的优势就是对我们的工作流非常友好,美术可以在没有任何逻辑代码的情况下,可以直接预览这些动作切换、动作融合,包括BlendTree这些东西都可以直接实时预案,不用运行游戏。对策划帮助也比较大,策划其实可以在这里做各种拓展。
对程序的好处一个是在这个动作融合的BlendTree上,《鬼泣-巅峰之战》用的BlendTree会比较多,牵扯到很多层,整体来讲效果比较好,另外一个就是,包括动作状态的分层,如果我们自己去做我们相对来说有一些难度,效果可能也没有它的这么好。觉得这块给我们的帮助是最大的。
03
SRP底层渲染流程及原理
大家好,我是来自于Unity的高级解决方案工程师李中元,今天给大家带来的分享是《SRP底层渲染流程及原理》。
我们先来看一下整个SRP的简单的架构,首先上面的这一层是我们常见的URP跟HDRP,还有自己去扩展的一些自定义的渲染管线,一般这些上面的渲染管线会依赖于SRPCore,SRPcore为我们提供了一些Common库,工具函数,还有ShaderLibrary,再往下是其实是我们UnityC++层面的一些东西,包括我们给大家提供的Context,Culling,还有各种Draw的函数,当然了还有SRP独有的SRPBatcher。
这部分对大家来讲更多是一个黑盒,今天分享的目的就是为了帮大家把这个黑盒打开,通过理解Unity在底层做了什么事情,大家可以利用这些信息去更好的优化自己的项目。
我们先来看一下今天分享的内容,今天分享的内容包括三个大的部分,一个是culling,就是ScriptableRenderContext.Cull,Culling就是我们在去调Cull函数的时候,Unity底层会做的一些事情;还有我们的Draw,Draw其实包含我们调用的CommandBuffer.Blit、CommandBuffer.DrawMesh这些常见的用的API,还包含DrawRenderers、DrawShadows这些API等等;最后是Submit。
我们先来看Culling,Culling分两部分,一个是Culling的过程,还有我会跟大家引入两个概念,一个是RenderNode,还有一个RenderNodeQueue,我会跟大家讲清楚,Unity为什么会引入这两个概念。
我们先来看一个小DEMO,左边是场景中灯光的设置,我们看到场景里面有四盏灯光,每个灯光其实都是实时的灯光,然后每一个光都会产生阴影。
我们每一个场景里面的每一个物体都会产生阴影。
通过我们运行这个DEMO查看Profiler的Timeline视图,能看到整个Culling的过程是这样的,看上去比较复杂,我们先一点点看。
首先我们来看这个红框框起来的部分,这部分是Shadow的Culling。
我们先来尝试着把场景中四盏灯光中其中三盏灯光的产生Shadow的选项改成NoShadows。
再回来看一下,这个时候我们就发现整个的ShadowCulling的Job从四个减到了一个。我们可以看出Unity底层会为我们每一个产生阴影的灯光创建一个Shadow的Job,去裁减我们整个的Shadow。
再来看一个小细节,下图箭头指向的标签——CullShadowCastersDirectionalDetnail。
这个跟什么有关系呢?我们继续修改我们场景里面的设置,我们把场景里面物体的会产生阴影的选项给关掉。
这个时候再运行DEMO看一下,这部分运行的开销也小了。
到这里我们能够发现,如果我们想去减少整个阴影部分的裁减开销,首先要去检查一下场景里的灯光是否有必要产生阴影,其次要去检查场景里的物体应不应该产生阴影,如果发现不合理的应该把这些选项都关掉,这样阴影裁减这部分的开销就能够降下来。
我们看完了阴影部分的裁减工作,我们再来看一下左边红框里面的部分,这部分是做什么的?
这部分其实是在裁减场景里面的动态物体,这几个job会产生一个IndexList这么一个数据结构,在IndexList存的是什么东西呢?Unity内部维护了一个场景里面所有renderer的列表,IndexList存的其实是当前可见的renderer的数组下标。我们通过一个例子来看一下这些job是怎么工作的。
在这个例子里面,上面的部分从0到19,这些其实就是Unity内部维护的endererlist,然后我们产生了四个Job,分别处理List里面不同的部分。大家能够看到底下,底下就是我们IndexList,对于IndexList这里能看到一个特点,就是这个list跟我们Renderer的list是等长的。
我们每一个Job处理裁减的时候,比如拿橘色Job作为例子,我们能看到在这个里面第四个是不可见的,这时候CullJob就会把0、1、2、3写到IndexList里面去。我们再来看一下紫色的Job是怎么做的,7和8中间这两个元素是不可见的,我们应该是把三个Index写到IndexList里面去。但是,它并没有在3后面插入我们的,而是在自己的区间,比如说它只会访问我们的5号到9号的IndexList,所以说第一个能访问的IndexList是5。所以,它把写到了这个位置,后面也是依此类推的。
这部分为什么Unity这么去做?
首先每一个Job都是在不同的线程执行的,如果说每一个Job去获取的数据,他们中间没有任何的交集,我是不需要引入锁的,所以他们访问的所有的数据都是线程安全的。
当我们写入IndexList的时候也是同样的。当我们写入的时候,橘色Job只会往这四个里面写数据,紫色往这里面写数据,我们在写入的时候也是不用考虑锁的,每个Job读写的数据都是独立的,所以我们的job里面是不需要给任何数据加锁的,这个就保障了我们Culling过程的速度。
但是这会带来一个小问题,就是我们产生的IndexList本身是不连续的,我们能看到和后面中间会插入一个0,9后面有两个0,这两个数据是不可用的。
Unity后面会怎么做的?在CullingJob完成之后,会有一个