去年,古风模拟经营类手游《江南百景图》成功破圈,成为年度现象级爆款。如何将它搬到小游戏平台?是转换还是重写?使用哪些技术方案,能在包体大小仅为原版1/20的同时,达到与 App 版相当的游戏体验?椰岛小游戏研发负责人大城小胖,带着他近300页的 PPT,在 Cocos 的两次线下活动中做了全面的技术分享。

转换 or 重写

《江南百景图》App 版游戏包大小有 600+M,上线前期还有部分用户反映游戏运行时手机发热严重。而小游戏版在经过立项选型后,决定使用 Cocos Creator 重写,仅用了1天就做出了 Demo。经过4个月的优化,我们最后将包体压缩到 30M 左右,同时保证游戏体验与 App 版相当。

优化的过程中,我们也做了以下工作,其中 代码 部分需要重新设计和编写。

渲染优化

原生版本的《江南百景图》移植到小游戏首先需要解决的就是 耗电高、易发烫、Draw Call 高等问题。

合批

合批是降低 Draw Call 最快也是最有效的方式。优化同样的 Texture,将多张的图片合并到一张图集上,这样不论要生成多少张不同的图片,都不会打断合批渲染,Draw Call 也就降低下来了。

但是《江南百景图》的资源非常多,每个玩家使用资源的顺序也不尽相同,如果玩家使用的资源分别在不同的图集上,还是会导致合批渲染被打断,产生 Draw Call。因此,针对这一情况,我们采用了 Multi-Texture 的方式进行了优化,其原理是将传统的判断是否在同一张图集,转换为判断是否在 同一批图集,这样就大大减少了 Draw Call 产生。

另外,通过 gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) 这个指令,可以知道在一台设备中 Shader 最多支持几张图集。测试发现目前 90% 以上的手机至少支持8张,因此我们将批图集的数量设置为8张。因为一个批次有 8 张图集,所以我们是通过这个 idx 判断某张图用的哪个图集,代码也很简单。

动态合图

小游戏版本采用了 Cocos 的动态合图机制,这样在 CDN 下载的图片也能进行合图。而为了提高合图的效率,避免浪费空间,我们会将长度或者宽度特别大的图片进行裁剪。

例如左图中的旗杆,由于图片太长,在动态合图时会导致空间浪费,因此我们将这张旗杆的图片裁剪成两张,如右图所示,再在项目中进行拼接处理。

采用同一个材质资源

在《江南百景图》中,玩家移动地图时,原本在显示范围外的图片将从水墨色变为彩色。

传统的方案是改变图片材质,当地图移动到要显示的节点时,节点一个一个地进行材质的切换,达到一个 “淡入淡出” 的效果。但是在项目中尝试之后,我们发现这样会导致 Draw Call 上升,而且拖拽地图又是一个很频繁的操作,游戏中实际效果较差。

因此在这里我们将所有城市物体资源,无论是人物还是建筑、常态还是淡入状态,都用统一的 Material、并使用顶点数据传递“时间参数”,以此节约性能消耗,最终达到所有建筑和人物的创建、移动、销毁等全都只需要一个材质就能够完成。

很多人会觉得一个普通的图片也用这么复杂的方案,会影响性能,导致性能变差。但是实际测试效果并不差,这也告诉我们,在游戏开发中还是要以实践为准,不能想当然。

优化 Shader 的输入数据

由于《江南百景图》的图片资源中不会用到 Color 这个属性,因此在材质中,我们将原有的 Color 数据去除掉。

下图是一个正常的顶点数据:

接下来将原有的 Color 数据去除掉,用来存放项目中所需要的其它信息,这样可以减少 CPU 与 GPU 互相传输的数据量。

层级规划

我们将不同的类型的资源,分别放置在对应的层级中。《江南百景图》共分了13个层级,下图只展示了部分比较重要的层级:

其中比较有意思的是旗帜层。旗帜是《江南百景图》中的一个常见元素,但因为项目实际技术限制,无法将一个旗帜制作在一个完整龙骨动画中,如果强行放在一起,就会导致在渲染到旗帜的时候出现断批。我们采用了 动态组织层级关系 的方式来解决这个问题。例如这是一个原来的旗帜预制体:

采用 动态组织层级关系 的方式,将旗杆与旗面拆开,旗杆放在下面的普通建筑物层,旗面则单独分为一层旗帜层放在上层,这样就很好地避免了渲染时一直被打断合批的情况。

UI 渲染优化

UI 部分我们没有使用动态合图或者 MultiTexture,动态合图我们留给了游戏中的人物和建筑、而没有使用 MultiTexture 主要是开发成本的原因。但在我们的优化下,现在游戏的 Draw Call 可以降得很低。

UI 方面我们也是做了分层,比如下面左边的图上我们的 button 层,里面都是按钮部分,右边是我们的标签牌层级。这样我们就可以根据功能区去划分图集,然后和游戏里的层级对应起来,而不会打断合批。

自定义引擎

Cocos 是个开源引擎,我们可以根据项目的实际需要,对引擎进行定制、修改,从而达到更好的效果。

增强 TiledMap

我们在 Cocos Creator 原有的 TiledMap 组件的基础上,拓展了新的功能,下图是 Cocos 自带的组件。

这里就不详细说了,有兴趣的可以去官方文档查阅,我们主要来说一下经过拓展的新功能。

1.  Diamond Tile:游戏中使用了很多 TiledMap 中的图块菱形方块, 但是引擎默认的传递方式是矩形,这样就会造成数据浪费和冗余。

这些图片首先都是 规则的菱形,所以很简单,直接 根据宽高进行进行计算。

将菱形周围多余的部分切割,这样很明显图片大小减少了一半,这里注意一下非标准图形就不能这么用了。

2.  Share Culling:《江南百景图》共有三层 TiledMap 地图层,勾选时 将只对 TiledMap 的第一个地图层进行处理判断可视区域的范围,而其他的地图层将直接照搬第一个地图层的处理结果,这样能够节约不少性能。

3.  With Color:如果不需要颜色数据就可以勾选,减少数据量的传输。

将道路转为 Tile

游戏中的道路是不需要进行淡入淡出效果的,如果当作普通建筑物资源来用之前的材质进行渲染,会消耗相当多的性能。因此我们将道路作为 Tile Map 地图的一部分,让道路不需要用之前提到的材质进行渲染。

还有一个小细节,在 Tiled Map Editor 中设置的宽高,与实际项目中使用是无关的,因此在生成的时候可以将地图块按照实际项目需求进行缩小,减少资源使用。

资源压缩

将一个原版 600+M 的游戏压缩到最终的 30M 左右,资源的压缩工作必不可少。我们需要将游戏资源进行合理的压缩,使其更加适合小游戏运行,并且不影响游戏最终的显示效果。

图片缩放

对不同类型、不同清晰度的资源,我们可以设置不同的缩放比例。我们将大部分的建筑缩放到原来的 0.65 倍,背景中的山川则被缩放到原来的 0.3 倍。另外,就算是相同位置上使用的人物立绘,由于每个人物的自身和背景的颜色、精度不同,也都可以给它们设置不同的缩放比例。

于是我们将所有 Sprite 组件采用 Custom 模式,可以自由控制比例。不同的图片使用差异化配置,设置不同的缩放比例,用脚本控制缩放比例,这样便可以打包出任意画质和体积的各种版本,并且还提升了动态合图的利用率和部分性能。

图片减色

综合比较了大家比较熟知的 tinypng 和 pngquant 两种工具之后,项目最终选择使用 pngquant 对 PNG 图片进行批量压缩。pngquant 可以自定义压缩品质,而且 pngquant 开源,容易维护,风险可控。pngquant 也提供像 ImageAlpha 这样的工具,可以实时查看图片减色后的效果,方便调整参数。

pngquant 地址:

https://pngquant.org/

需要注意的是,由于 Cocos 会进行合图处理,如果对 Build 前的图片做压缩,合图时前期的一些压缩工作可能就此无效化,所以我们要对 Build 后的图片 做压缩处理。

另外我们也建议程序多了解一下图片格式以及其原理。不是所有图片都要使用 PNG 格式,也会有使用 JPG 的情况。

场景剔除

这部分我们的需求是 只渲染可视物体。那么用什么方法确定哪些物体是可见的呢?最开始我们使用了四叉树,但是在 JS 语言中的效果并不好。所以我们给地图划分格子,Grid 的单元格大小要适中,但单元格的边长应为 2的整次幂,便于利用 位运算 提升性能。

如下图所示,红框就是镜头,所以需要渲染的也就是这个红框里出现的格子。然后我们再根据建筑物的坐标、大小去进行计算,判断建筑在哪一行哪一列的格子里,从而确定该建筑物是否是需要被渲染的物体。

这是一段简单的检测函数 大家可以根据自己的项目需求去进行扩展。

除此之外,为了防止特殊情况出现,判断的可视范围需要比实际范围更大一些。

寻路

《江南百景图》使用的寻路算法,有针对单源单点的 A* 和单源多点的 Dijkstra。但这里我们要讲的不是寻路算法,而是在游戏中的用法优化。

针对地图很大、建筑物和人物都很多的情况下,这些算法一起执行就会很损耗性能。所以我们用了 分时寻路,就是把寻路过程由一帧分到若干帧去进行计算,这样就不会在某一个时间段集中进行大量运算,对游戏性能也不会有太大的影响。

除此之外我们还在游戏里做了一个大胆的优化,就是统一管理寻路任务,同一时间只为一个角色服务。也许有人会问,那岂不是一个角色在哪里走、其他对象都在那边等着?其实真正在游戏里不会有这种奇怪的表现。首先每个角色寻路的起始和结束时间都不一样,再者这个同一时间是非常短的,就等于把角色寻路分配到了不同帧里,交替进行执行。

再谈性能

模糊特效

玩家在打开《江南百景图》的任意界面时,游戏的背景需要做模糊处理,而背景中的人物动画等仍需要正常播放。

在经过一系列的研究(可参考 PPT 中资料)后,我们选择了一个较好的方案,将场景渲染到一个小的 RenderTexture 上,然后将其通过 Kawase 模糊后再放大 显示,如下图所示。

RenderTexture 池

在小游戏或 Web 端 创建 RenderTexture 时,比较损耗性能。所以我们在游戏中使用完 RenderTexture 后,不是直接销毁,而是将其放在一个 缓存池 中,下次从缓存池中调用符合要求的 RenderTexture 即可。

点击检测

《江南百景图》中有很多建筑物,而在用户点击时,并非简单地通过地形上的块做判断,而是给每个建筑物画了一个 多边形检测区域。但是建筑物是移动的,如果 多边形检测区域 也随之移动,从性能和逻辑上都不是好的处理方式。

于是在实际操作中,我们让建筑物移动,而对应的 多边形检测区域 不做移动,并将其设置在原点坐标上。用户点击操作时,将点击的坐标减去建筑物相对原点的坐标,就可以进行点击检测了。同理如果建筑物是反转状态,可以将点击坐标进行镜像,而 多边形检测区域 仍然可以不做调整。类似还有其他情况,大家也可以去了解一下各情况下对多边形的处理方式。

数组排序

数组排序是大家容易忽略的一个优化模块,Array.sort() 这样的 快速排序 算法,更适用于混乱无章的数据。而在《江南百景图》中,每帧都会对场景中的人物和建筑物进行排序,而连续的两帧之间差异不会很大,也就是 相对有序 的数据,而这更适合使用 插入排序 算法。

其他优化

「阅后即焚」

游戏中存在一些低频显示的大图,例如进入游戏时的公告、抽到的卡片等,玩家在游戏中看一遍就不会再出现了,对于这一类我们用了“阅后即焚”的思路。

像这些大图,我们通常先从远程服务器下载到本地缓存,产生 Image 对象,还有cc.Texture2D、renderer.Texture2D

我们通过伪代码来简单讲解一下。加载图片时,将图片添加到我们自己创建的回收用工具类 TextureRecycle 中。

视图关闭时,通过工具类回收这些图片。

在图片的回收阶段中,就可以将以上所有用到的对象都清理干净了。

构建优化

在构建发布流程中,项目使用了大量的自动化脚本来优化构建流程。包括 全平台构建、上传游戏平台、资源预处理和后处理、CDN 同步和版本控制和二次混淆加密 等。但成也脚本败也脚本,过长的构建时间也造成了不少困扰,因此我们也需要做一些额外优化。

Cocos 新版本添加了一个第三方开源压缩工具 Sharp,压缩级别是0-9,数值越大压缩越久,Cocos 的默认参数是 6。由于我们已经进行过 图片减色 处理,因此我们将参数改为 0,这样就能减少很多构建的时间。

而各平台构建时间总是格外漫长,原因是在每次平台构建时,Creator 都要重新生成对应的平台图集。找到原因后,我们在每次构建前,将对应目录中的 info.json 中的 actualPlatform 参数先修改为 对应的平台名称 再打包,这个改动使我们的构建时间由之前的 15 分钟缩短到 10 分钟左右,提升了 30% 效率。

在不懈的优化下,我们看到在现场演示时,这个用于官方演示游戏的高级账号,在游戏场景人物都很丰富的情况下,仍然只有 6个 Draw Call


篇幅有限,本文仅提炼技术思路与亮点,更详细的技术实现内容欢迎点击【阅读原文】移步 Cocos 论坛讨论帖,一起学习交流。

帖子链接:

https://forum.cocos.org/t/topic/121618

同时,大城小胖也将本次《重绘<江南百景图>》PPT 完整版分享给大家,感兴趣的小伙伴可以扫描下方二维码添加 Cocos C姐 微信免费领取。再次感谢大城小胖的倾情分享!

>>微信 ID:Cocos_Cjie

往期精彩

如何重绘「江南百景图」?近300页 PPT 免费分享!相关推荐

  1. 《江南百景图》为什么火了?和主创团队聊完之后,我跪了

    自7月2日上线以来,<江南百景图>便一直占据着iOS免费游戏榜的冠军.作为一款备受青年人喜爱的手机游戏,<江南百景图>带来的水乡风光和市井气息,洋溢着浓浓的国风,鲜活而且稀缺. ...

  2. 江南怎么用计算机弹,怎样在电脑上玩江南百景图

    <江南百景图>是一款古风模拟经营类手游,由<超脱力医院>原班人马打造.游戏玩家需要扮演文征明,重绘明朝的江南盛景,打造专属于自己的江南百景图.很多人沉迷其中,经常一肝就是大半天 ...

  3. mfc 饼图绘画_每周推荐|江南百景图放置类佛系游戏,慢慢玩才是乐趣

    中文名:江南百景图 开发商:椰岛游戏 游戏类型:模拟经营,古风建造 游戏平台:IOS.Android <江南百景图>是一款由椰岛游戏自主研发的古风模拟经营建设类手游,自从2020年7月2日 ...

  4. 江南百景图凭什么火?

    江南百景图火了. 先引用几个数据: 1.游戏免费榜连续25天前三,没有掉下来过. 2.游戏畅销榜稳定前50,近期维持在20-30名左右. 3.冲上总榜前十,15天:前三,10天:第一,4天. 4.日均 ...

  5. 椰岛CEO:《江南百景图》的立项过程

    在TapTap开发者沙龙现场,Wesley对外分享了<江南百景图>的具体立项过程,以下为演讲实录: 大家好,我是椰岛的CEO Wesley,我在椰岛主要负责产品,包括一部分的运营工作.&l ...

  6. 《江南百景图》游戏设计小思考:留边占角“小烦恼”

    <江南百景图>是一款模拟经营游戏,它解锁新的地块儿并不像其他"大规格"的游戏那么整齐有序,而是"留边占角",不甚规则,这是为啥呢? 游戏是以江南水乡 ...

  7. 《江南百景图》,解谜经营背后的逻辑

    <江南百景图>是一款椰岛游戏出品的模拟经营类手游,游戏中,玩家将梦回明朝江南地区,成为城市的设计师,描绘蓝图.兴造建筑.规划布局,经营赚钱:另外还有奇遇探险等多条支线,安排居民起居工作,玩 ...

  8. 在Unity中模仿游戏《江南百景图》中物体的出现效果

    在Unity中模仿游戏<江南百景图>中物体的出现效果 1. 效果 1.1. 游戏中的效果 1.2. 在Unity中模仿的效果 2. 思路 3. 实现 3.1. 图片 3.2. shader ...

  9. 江南百景图显示服务器错误,江南百景图通讯失败请保持网络畅通并重试

    江南百景图出现了通讯失败请保持网络畅通并重试的报错,导致许多玩家一直进不去游戏,遇到通讯失败的情况该怎么办呢?下面小编就向大家带来江南百景图通讯失败的解决办法汇总,赶快来看下吧. 江南百景图通讯失败请 ...

最新文章

  1. Python之初识函数
  2. 机器视觉中彩色成像必须考虑的十个问题
  3. 海报推广神器:活码加多级加密跳转防封双重保护
  4. 企业日志分析 五大问题需重点注意
  5. java mysql unix_timestamp_MySQL unix_timestamp()函数
  6. De 30: Decoupling Linear Systems with Constant Coefficients
  7. linux终端字符串转字符画
  8. Ubuntu使用systemd设置开机自启动
  9. c语言——求逆矩阵,伴随矩阵,行列式
  10. TP5使用easywechat进行微信Native扫码支付
  11. No matter how hard it is or no matter how bad it gets, I am going to make it!
  12. #CSDN软件工程师能力认证学习精选# NoSql是什么?
  13. 还在用PDF做简历?落后了!
  14. python财务预算分析_财码Python管理会计小实验—滚动预算vs定期预算
  15. 关于MaxCompute的基本了解
  16. 浙江大学计算机学院 潘刚,潘刚 - 江苏科技大学 - 生物技术学院
  17. Git版本控制__分支管理
  18. ULINK的手动刷新固件
  19. np.array(A, ndmin=2).T中的T是什么意思
  20. 人脸表情识别系统的设计与实现(含UI界面,有完整代码)

热门文章

  1. ERP WIP 部分API应用 详解
  2. fail2ban 使用
  3. [4G/5G/6G专题基础-147]: 6G总体愿景与潜在关键技术白皮书解读-2-6G发展的宏观驱动力
  4. 硬件学习笔记(器件篇)—— 铝电解电容(四)
  5. LINK : fatal error LNK1168: cannot open Debug/c_pub_test.exe for writing 解决方法
  6. 以太坊 r,s,v是什么,以及怎么实现的
  7. 该邮件的附件格式不正确_刚刚海关退单时提示430,该怎么做?一定是你打开的方式不正确!!...
  8. DM MPP集群(两节点主备)
  9. 基于tushare的股票数据构建1
  10. 跨平台应用开发进阶(二十六) :忐忑悲壮路,心酸出坑史——记第一次iOS艰辛上架路