https://zhuanlan.zhihu.com/gu-yu
前文回顾:The world at your fingertips — 天涯明月刀幕后19(首测和植树)
Paint the sky with stars
场景的光照,由于没有了间接光照,变得更为简单扁平,我们需要一些特性能弥补不足,让场景有更多的变化。
天气姐姐在第一次测试中搞定了普通天气,做了点强化后,就开始研究日夜变化。单纯看日夜变化,其实还好,根据大气散射,算出各种光照变化,修正场景里的光源就可以了。但真正复杂的是工程上的综合考虑。比如下面一些问题,就很让大家挣扎和辗转了一番。
首当其冲的问题是,这么多场景、物件,如何确保在所有场景都能正常工作?天刀的材质系统用了PBR,这是tough哥在项目开始时候的坚持。在日夜变化的时候,我们得到了巨大的回报,各个物件只要材质调整正确,在不同的光照下也会更自然,让日夜变化成为了可能。
初步解决了场景的正确显示后,我们要考虑光照如何在编辑器里面调整时间,如何触发变化。这个也不难做,做几个时间段,每个时间段做一组参数,然后根据时间段进行插值就好了。想来简单的事情,做起来也并颇犹豫了一番。大量的日夜光照参数,是否可以简单的线性插值,效果是否如预期的好呢?能不能有那种过度的效果呢?谁也没有把握。美术说,先做起来试试看,大不了多做几个关键时间段,肯定能调出好的效果。
随后阴影变化成了一个问题,Tough哥已经针对CSM做了大量的优化,预先Cache了很多shadow map的计算结果,如果阴影方向时刻变化,那么这些优化就很难生效了,于是我们做了点取舍,隔一段时间更新阴影方向,虽然效果不是最理想,但也取得了性能和效果的平衡。另外一个问题是,太阳角度如果特别接近地平线,那阴影就会拉到无限长,相当于shadow map平时整个场景,视距很大,渲染性能极受影响。那我们就限制一下最低的阴影投射角度,太阳可以继续落下,但我们的阴影投射光到了某一个角度就不再变低,这不正确,是一个工程上的折中。
然后游戏逻辑上也要考虑,晚上是不是某些地方应该点灯,随着日夜变化,有些地方要不要补光。下雨的时候某些重要的NPC要不要打个伞?雨停了要不要收了?都是很琐碎的细节,我们做了一些trigger来根据天气情况动态改变,当然限于工作量,无法尽如人意,也只能尽量显示一下诚意了。
但还没有结束。美术团队和天气姐姐忙了很久,大致搞定,大家试玩,普遍表示,很不喜欢晚上的效果。国外做游戏,强调质感,喜欢黑乎乎、脏兮兮的场景,而国内玩家的审美,偏好干净明亮的场景。黑夜灰蒙蒙的,看不清,容易感到压抑,不利于MMORPG的长时间游戏。我们又只好做了些妥协,时间是不均匀的,黑夜很短,看个技术效果即可,白天不成比例的长,保证愉快的游戏。
日夜变化是一个无底洞,永远没有结束的一天。有了日夜变化,日出日落不做点特殊效果也就说不过去了。天气姐姐就开始研究日出日落,在夕阳西下,把场景均匀的,慢慢的,染成一片晕红色。
进一步可以提升的,是天空盒。朵朵的白云飘来飘去,看久了也觉得有点厌倦,我们想要有更动态的云。天气姐姐随后又看了体积云,云的形态随着时间有些变化,边缘隐隐透出日光照耀,煞是好看。
夜晚是另一种风格,月圆月缺,被计算和实现,不同时间,月色的盈缺会有变化。场景泛出悠悠的蓝,夜色漾着微微的影。星夜又怎么能没有星辰,天气姐姐又开发了星星的系统,银河也在合适时间显示在天空。
一测就有的雨雪天,也得到了进一步增强,闪电,暴雨,程序开发了更多的特性,美术调出更多气候的参数,天气系统得到进一步的提升。
整体的天气、日夜系统,经历了好几次测试的持续改善,一直在缓慢坚定的前行。在后续的很多次测试中,我们看见过玩家们忙碌着自己的升级,突然有人大叫杭州城下雷雨了,然后身边的玩家纷纷传送到杭州去,站在最高的屋顶看雨打风吹,雷鸣电闪;我们也看见过夕阳西下,远离战场喧嚣的山丘,三三两两坐着玩家,面对夕阳方向,打坐发呆。看落日,看银河,看日出,宏大场景配合日夜星辰变化,在工作量可控的情况下,场景的丰富度和多样性被展现出来。所有的系统在美术团队手上整合,演进,一次又一次让程序同学惊讶,原来一样的系统,能在他们那里发挥如此的效果。
多层碰撞系统
碰撞系统也是一个让人头痛的问题。
前文提过,第一次测试的碰撞系统是一个半成品。我们的产品并没有人和人之间的碰撞检测,也不需要FPS那样精确地碰撞判定,所有的碰撞,都是基于高度的格子。类似Terrain的碰撞方式,只是在服务器上并不存放terrain数据,我们简化成了格子,每个格子有一个固定的高度。找了个精度和存储空间能接受的格子大小,就一路做下去了。
这个系统优点是效率极高,而且服务器有相当多的Legacy代码都是用类似系统,节约了很多的开发成本。缺点也非常多,内存开销大,不够现代,所以我们都不好意思出去和人打招呼,灵活度不够高,最致命的是,这只是一个单层的碰撞。无法处理同一个位置有几层不同高度的碰撞。
经过数百次PK,尝试推翻目前体系,使用客户端常用的碰撞系统,但最终未果,只好妥协,说既然产品时间紧张,我们还是延续目前的方案吧。但多层碰撞还是需要加的,因为有轻功系统的存在,我们能去的空间很大,如果没有多层碰撞,我们要么不能去屋顶,要不不能进房间,这都是我们不希望看见的。
产品开发就是那么无奈,技术上没有对错,激进有激进的好处,保守有保守的理由。
永远在抗争,常常有道理,总是要妥协。
在现有的uniform grid方案上,大家想了一下,决定还是生成一些多层的碰撞patch信息,在有多层碰撞点的地方,生成额外层数不定的碰撞信息。相当于把复杂的场景按需分层,然后标识出不同的层之间的高度。举个例子,就是在衣服上打补丁,需要的地方多缝一块布上去,就有了两层高度。比如一个亭子,其实是两层高度,地面有一层高度,整合在地形高度中,无需特别表示,亭子顶上又是一层锥形的高度图,标出xy坐标,作为一层patch放在地表高度之上。这个方案对已有旧代码都很友好,AI、技能、寻路都能经过很少的改变就工作。
又是一番讨论后,tough哥就开工了,打算生成这样的数据。作为一个渲染程序员,第一个想到的方法就是基于GPU来计算,大场景分块,从上往下渲染一遍场景,取出深度。再渲染,取出第二层深度,重复N次,就取出了各层深度。然后取出的深度其实是属于不同层的,还需要合并、整理。再找出层和层之间的连通关系。
这事情可比想象的复杂多了。Tough哥做了很久,工程上的挑战非常多,而且整理层数、连通关系计算等,是一个很复杂的逻辑,很难轻易想清楚、想全面,每次生成碰撞,时间长不说,总有这样那样的错误。遇到了错误,发现是算法问题,还需要修改算法,改完后回归测试又成了一个巨大的问题,怎么才能保证这么巨大的场景上,以前正确的碰撞信息,在新的算法下不出问题,这需要反复测试的,工作量很大。
期间有过一次对外测试,就并没有完全搞定碰撞系统,导致出去的版本,玩家走着走着突然掉到了地底,或者是平地上碰到了空气墙、空气柱,或者平面上有个小坑,卡住了等等。最恶劣的情况来自于长途自动寻路行走,往往玩家往一个地方走,预计要走几分钟,走开一会,回来一看,角色卡在离出发点不远的某处在徒劳的原地行走。
之所以会出现这么多问题,一是开发时间的确被低估了,项目开始的时候豪气干云的表示有多种可选feature我们不妨全做出来,但在那个时间点,已经没有那么大的自信和勇气了,项目规模一大,编译时间、和其他系统的协作关系、完整测试一遍feature的时间都变长了很多,自然而然就慢下来了;二是也是我们不熟悉的领域,之前没有做过这种,也没有找到太好的参考,所有的问题都是遇到了想解决方案,提前规划不够;三是开发辅助的测试资源不足,中间一度都觉得这个系统完成了,粗粗测试一下工作挺好的,后期整合完所有的数据才发现不对,这时候已经太晚了,怎么改都来不及了。
明白了问题,对症下药,时间不够,继续做,下一次测试重点调整这个,不管是生成碰撞还是服务器寻路客户端表现,都以这个特性为最高优先。没经验,就一起找参考。Tough哥是一个吃苦耐劳的同事,已经天天睡公司了,他不怕苦不怕累,就怕做得东西不上流。我们一起找了个技术包装,整个技术并不是为了生成过时的格子碰撞,而是使用gpu加速对游戏场景进行体素化的过程,这样就能找更多的参考,同一个技术主题包装后也闪烁着不一样的光芒,照耀Tough哥每天在公司的24小时作息。测试辅助资源不够,那就全面倾斜,优先测这方面特性,Dev tester随时待命,寻找地图中所有异常的碰撞,一一分析解决。
经历了很多辗转,面临了很多挫折,碰撞问题基本被解决了,但中间暴露出来的问题值得深思。技术的过分保守,规划的盲目乐观,资源的配置不足,导致寻路在测试中暴露出了问题。项目已经逐渐成长成一个依靠个人英雄主义无法掌控的阶段,后续的开发中依然可以有个人英雄拯救世界,但作为Leader,如何保证大家有技术发挥空间,并且降低项目的整体风险,还需要更慎重的做决定。