专栏首页Creator星球游戏开发社区2D MMO中角色动画的优化总结

2D MMO中角色动画的优化总结

在深圳Cocos沙龙上,有幸结识了社区中大名顶顶的Colin,Shawn在社区论坛上第一次看到Colin的团队用CocosCreator制作的《热血暗黑》时就被深深地震撼到了!更为重要的是,Colin将他的技术心得和宝贵开发经验写成文字,每一篇分享都是满满的干货。而且幸运的是Shawn得到Colin的授权许可,将他的文章散播到奎特尔星球,我们一起欣赏一起成长!

2D MMO中角色动画的优化总结

1

概述

我们的项目是传统的2D MMO,即人物动画是以图片帧的方式表现的,一个角色大约有8个动作,1个动作有8个方向,1个方向约有10到20帧的图片。这样算起来一个角色由近千张图片帧组成。这样的量对内存和性能都有很高的要求。从立项到目前经历了很多次优化,终于在流畅度和内存占用上达到比较理想的状态。

不知道群里有多少人也在用Creator做类似的MMO手游,我在这里把优化的经历大概写出来供大家参考,也许你们有更好的优化方案,都欢迎一起讨论。

2

最初的实现方案

最初为了快速实现效果,采有很直接的方式:

  • 一个动作一张图集(Plist),8个动作则有8个图集。
  • 每个动作只包含5个方向的图片帧,另外3个方向通过翻转实现。
  • 动画则用Creator提供的动画剪辑(AnimationClip)来实现。

由于一个AnimationClip只能播放一个方向的动画,那么一个角色就需要8*8=64个AnimationClip,如果每个动画剪辑都要在编辑器中编辑,估计美工人员会先晕倒了。

幸好.anim文件是json格式,很容易理解它的含义,于是我们用Python写了一个导出脚本,美术只需要提供角色的所有散图,脚本调用TexturePacker先合成图集,再动态生成anim文件,最后生成一个prefab。

这个prefab就代表一个角色外形。程序的使用就很简单,首次使用先加载Prefab并实例化出结点。从结点取出Animation组件并调用播放接口即可。当角色释放时,把结点收回存到NodePool去,以备下次使用。

3

显示易见的性能问题

从上面的实现看,其实不用Profile也很容易知道会有性能问题,虽然里面使用了NodePool缓存创建过的角色外形。但首次加载这些资源会很卡:一个角色的图集和动画剪辑加起来接近100个文件,想想那个IO压力都觉得蛋疼。

在PC的Web端,这个性能问题没有暴露出来;在安卓甚至苹果机上,一旦旁边有角色进场景,马上就能感受到卡顿,有时甚至能卡上1到2秒。因为我们做的MMO,场景中的玩家进进出出是很平常的,这样的卡顿是不能接受的。

4

优化之一:去掉动画剪裁

首先能想的是去掉anim文件,角色动画其实很简单,只是一些序列帧的播放,并没有用到移动缩放这些动画类型。用anim文件来描述动画有点浪费了,完全可以用另一个简单的Json文件记录动作的信息,比如这个角色有几个动作,每个动作有几个方向,每个方向有几个帧,只要这些信息就够了,类似这样:

{
	"run": [5, 20, 0.033],
	"idle": [5, 4, 0.1],
}

而图集中图片帧的名字,需要有一点规则,即可以通过动作名方向值帧索引这些合成。然后就可以找到对应的图片帧了。

在程序中,仍然是使用AnimationClip来实现动画,只不过它是动态创建的,使用引擎提供的一个API:cc.AnimationClip.createWithSpriteFrames,具体可以参考文档。AnimationClip不用一次性创建出来,可以在播放某个动画时再创建,这样创建的消耗就平摊出来了。我们实现了一个AnimControler组件,通过它来播放动画,而延迟创建动画剪辑这些就顺理成章就封装到组件里面了,外部逻辑不用关心。

这一轮优化下来,一个角色只有动作图集和一个Json文件,去掉了大量的anim文件。包体也因此少了30M大小,算是一个额外好处。

5

优化之二:去掉Plist文件

本以为经过上面的优化,手机上的卡顿应该解决了,但是很不幸,从表现上看只解决了1/3,仍然有0.5到1秒多的卡顿。接下来就各种脑洞和调试了,甚至怀疑加载图片是不是用同步的方式,后来问了@panda,以及自己查看引擎代码,终于确认用的是异步方式。这样的话不应该呀,难道解析Plist文件用了这么长的时间?

后来在调试原生版本时,发现Plist文件原来会按图片帧打散,一个帧就一个json文件,里面描述了帧的位置偏移等信息。想想我们一个角色有近千个帧图片,当加载Plist时,引擎要把所有的Json文件加载进来。这比加载anim文件可蛋疼多了。

既然发现了瓶颈所在,解决方案就自然而然了:去掉Plist文件,只留图片。那么怎么知道每一帧的信息呢,答案还是从Plist中找。我又用万能的Python写了一个工具,把Plist的帧信息提取到上面提到的json文件中去,然后把Plist文件删除。

在程序中,我不再加载cc.SpriteAtlas,而是直接加载cc.Texture2D,然后当创建cc.AnimationClip时,我需要从配置中找到cc.SpriteFrame的纹理信息,然后用:

new cc.SpriteFrame.create([Texture2D ] [rect ] [rotated ] [offset ] [originalSize ] )

动态创建帧,把这个帧传入动画剪裁。后面的播放就和原来的一样了。

经过这一次的优化,基本上把IO的负担都消除了,我们在IOS上测试了一下,果然如丝般顺滑,进打怪地图会刷出大量的怪物,也感觉不到卡顿。在安卓上也基本可以接受,在大量角色进来时会有很微小的卡,时间不会超过100毫秒,而因为有了缓存,后面也是顺滑的。至于安卓的这个微卡,我归结为两个:

  • 图片加载进来后解析成纹理的过程。png文件涉及到解压,反编码成raw形式。
  • json文件的加载和解析。json的加载其实是同步的,而解析成JS对象也是需要时间的。

上面这两个其实也和安卓的性能相关。

6

优化之三:压缩角色图片

我们的角色图片用的是png,如果把散图合成一张大图,大概接近于2048*2048,即一个角色加载到内存,会消耗10M以上的内存。再加上角色是可以换装的,一个场景同时出现10个以上的外形是很平常的。光角色这一块就会消耗掉100多M的内存。还是挺让人着急的。

考虑到我们的游戏类型是偏写实的,画面的彩色相对会复杂一些,就决定用PVR,ETC这些格式来看看效果。后面在构建过程中,加入了纹理压缩的流程,IOS用PVR4,安卓用ETC+Alpha,最后的效果完全可以接受,在手机的小屏幕上看不出太大的区别。

PVR4使内存减少了8倍,ETC+Alpha则减少了4倍,这个优化量太可观了,而且附加的好处也很多:

  • 加上压缩的同时用GZip对图片再进行压缩,图片大小比原来的PNG减了3倍,使得程序包又减少了几十M。
  • 由于压缩纹理直接传给GPU,加载纹理只有一个反解压GZip的过程,加载过程也必然大大加快。

7

最后的期望

都说内存,速度,和发热是手游优化的三座大山。但引擎是不大可能完全帮你解决这些问题的,最重要的还是要根据引擎的特点,自己研究出合适项目的优化方案。引擎只需要在底层提供好机制,具体策略由项目去考虑。

对于Creator后面的进化,很希望这几个方向得到加强:

  • JS引擎的效率,Web端的V8已经足够优秀,但原生的SpiderMonkey显然差了很多,官方似乎也在提升原生引擎的版本,期望后续的消息。
  • 类似Unity的AssetBundle,有了这个机制,就可以对资源进行分包,这对大游戏的分包发布很有好处。
  • 工程脚本的分割,游戏里面有大量的策划配置文件,有些动辄上万行,整个工程的脚本合并起来会达到好几M,这对热更新非常不友好的。如果能把脚本分割,每个脚本可以打一个Tag,相同Tag的脚本会合并,这样项目就可以根据自己的情况对脚本进行分离处理了。

最后给大家欣赏一下Colin与他的团队正在开发的游戏《热血暗黑》中的游戏图截:

本文分享自微信公众号 - Creator星球游戏开发社区(creator-star)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-11-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 2019-08-02hover.css 插件的使用

    用户4344670
  • webpack项目轻松混用css module

    本文讲述css-loader开启css模块功能之后,如何与引用的npm包中样式文件不产生冲突。 比如antd-mobilenpm包的引入。在不做特殊处理的前提下...

    我是leon
  • Android、IOS文字居中偏离的解决方案

    移动端开发,经常会遇到的问题,就是文字居中。一般都只能往css方向去fix这个问题。 自己以前也用过position:relative;top:-*px的方式去...

    我是leon
  • 动态加载css方法实现和深入解析

    此动态加载css方法 loadCss,剥离自Sea.js,并做了进一步的优化(优化代码后续会进行分析)。

    我是leon
  • CSS——loading动画

      由图可见,动画1中有三根竖线,在进行变长变短的高度变化以及线条的颜色变化,因此分为以下几个步骤:

    流眸
  • 金三银四,那浏览器兼容你知多少?

    它表示所附加的声明具有最高优先级的意思,被浏览器优先显示(ie6不识别此写法) 语法:选择符{属性:属性值!important;}

    拾贰
  • 源码解析-url状态检测神器ping-url

    ping-url是我最近开源的一个小工具,这篇文章也是专门写它设计理念的科普文。 为什么会做这个ping-url开源工具呢? 起因是:本小哥在某天接到一个特殊的...

    我是leon
  • 使用nginx缓存服务器上的静态文件

    如图所示,nginx缓存,可以在一定程度上,减少源服务器的处理请求压力。 因为静态文件(比如css,js, 图片)中,很多都是不经常更新的。nginx使用pro...

    我是leon
  • 获取元素的最终background-color

    用JS代码求出页面上一个元素的最终的background-color,不考虑IE浏览器,不考虑元素float情况。(题目copy自网上)

    我是leon
  • vue中使用iconfont

    用户4344670

扫码关注云+社区

领取腾讯云代金券