手游性能优化

前言:优化往往都存在着取舍,只有极少数优化是只有好处而没坏处的,更多的优化一般都会带了其他地方的一些坏处(就等于程序常说的时间换空间,空间换时间),而之所以称之为优化,是因为往往利远大于弊,可能少量的空间就可以换取大量的时间,甚至可能付出的是大量的空间换取回来少量的时间(可能我有大量的内存剩余,内存不吃紧的情况下其实这样还是可以的)。所以下面聊到的优化,具体要结合实际项目情况来做取舍,如果麻木的运用下面的优化,可能结果甚至会更差,如:
曾经,有人调用了某些函数,把一些Shader的处理放到了GPU进去,减少了CPU那边的运算压力,效果很明显(帧率从20左右,又重新回到了正常的60帧不掉帧了)。可你不结合实际,麻木的去按照这些优化去做,而你项目恰恰吃力点在于GPU,相信这“优化”出来的结果会变得更加糟糕。
1.游戏启动速度优化
(1)本来把很多的管理器,还有配置表,甚至还有一个lua文件(require了游戏内所有的lua文件)都在启动时加载了,后来尽量都把这些能砍的都砍掉了,尽量把东西的初始化都放在了登陆的loading界面。
(2)剩下砍不掉的,还是占用时间的,在游戏启动时,第一时间先放公司的logo图片,其实下面就是在偷偷的加载那些内容了。所以公司的logo图片在不同手机上显示的时间也不同(看手机加载速度),但对于玩家来说,他们只是以为在等待一个正常的公司logo显示。
2.登陆时预加载的内容
包括但不限于音乐音效,龙骨,配置,纹理,代码等等。但绝不能麻木加载,还是取舍问题,要结合游戏的实际情况来进行预加载。
(1)游戏里经常用到的资源,我们进去预加载并且不释放这类资源,让他全局存在。如主角的角色龙骨,你打开10个界面可能有6~7个界面都有他的存在,每个界面打开关闭都进去加载与释放,耗时不说,频繁的申请和释放内存还可能会产生不少的内容碎片。
(2)一些使用率极少而又很很占内存的,我们还是用到时才加载,如背景音乐,假设100种不同的战斗地图有100个背景音乐,但玩家通常1次只会玩上3~10种地图,而且背景音乐又大,同时也只放1首,所以这种东西用到时再加载就可以了。
(3)加载资源时,要进行分帧加载,一般登陆的loading界面都会有一些界面动画以显得界面没那么无聊,但如果加载资源时占满了主线程资源,那会导致动画看上去很卡很卡。
(4)向服务器获取相关游戏的基础数据信息,并且对一些公用模块进行初始化。
3.通过软件检测着游戏运行时内存变化
(1)检查一个系统打开,内存加了多少,关闭后,是否内存能降回来,如果比界面打开时内容高,是哪个地方没释放到内存(存在内存泄露)。
(2)留意有没地方存在内容峰值,是否需要通过分帧,延迟加载等手段来降低内存峰值。
4.多线程的运用
在单线程模式下,东西是顺序执行下去的,A命令没结束就轮不到B命令的开始,此时就会卡死主线程直到A命令结束。所以为了让主线程流畅的运行下去,一些不是必须在主线程执行而又会占用线程大量时间的任务,我们可以在分线程下进行操作(如:读写文件,查询数据库,访问网络需要等待数据返回)。 而如果要操作多线程,一定要万分注意加好“锁”,和进程不一样,线程是共享数据和内存的,一旦两条线程同时修改同一个数据,后果难以料想(很多游戏出问题都会崩在线程上,所以这个也要慎而用之)。 而如果你的游戏有需求频繁的创建线程,可以写个线程池,动态的分配线程,这样就不用频繁的创建线程了。
5.减少图片占用的内存
1.像ListView这样的列表控件,可能打开界面时,控件内就创建了几百上千个item,而界面需要显示的则只有10来个item,所以我们可以只动态创建界面上显示的10来个item,再额外创建多屏幕上下各1个item,当屏幕向上滑动时,把屏幕外最上方的item移动到最下方然后刷新item里面的东西,这样就不用在创建界面时就创建无数个屏幕上看不到的item(包括上面的图片)。
2.尽可能减少原图片的尺寸。按照游戏里需要显示的图片来出图,例如界面只需要显示100100,原图却是200200就浪费了大量内存了。
3.假如像房间里要选地图时,要显示地图的缩略图,可以让美术重新出一批缩略图资源,因为选图是列表,如果把一批地图以原图方式加载进来再缩小,这内存消耗是巨大的……所以,取舍取舍,情愿出一批很小的资源,虽然资源有点重复让包体大一点点,但可以省下了大量的内存开销。
4.对于一些要重复创建多个的图片,可以先把纹理缓存到内存中,使用时再读取缓存的纹理,创建对象。 如果每次都新创建对象把相同的图片纹理重复保存到内存中,这样就会吃掉更多的内存了。
5.降低通道位数,例如把RGB888格式的图片转换为RGB565格式。
6.使用纹理压缩,如etc,pvrtc等,但需要设备上支持对应的纹理解压。
7.如果散图打成图集,可以设置使用npot(图片不需要2的幂大小),可以大量减少图集的空白区域,让图集充分填充,让最终图片更小。
8.如果像背景图那种不需要设置透明度的图片,可以使用jpg,相比较于png一个像素需要4个字节,因为jpg没透明通道,所以一个像素只需3个字节,会更省内存(如果游戏引擎读图使用RGBA8888则即使jpg只需3字节,但读进内存时依然占用了4个字节。
9.加载图片时最好按照从大到小的顺序加载图片(可更有效避免内存峰值)。
参考资料链接:https://blog.csdn.net/weixin_32208747/article/details/81979066?utm_source=blogxgwz8
6.纹理加载方式
1.所有纹理都加载到GPU中(好处:速度最快,无需从CPU拷贝数据到GPU,直接在GPU中绘制)。
2.所有纹理都加载到CPU中(坏处:所有纹理都提前加载进内存,占用内存量比较大)
3.部分纹理加载到CPU中
我选择了第三种方式。把常用的纹理数据加载到CPU,然后等运行需要使用时,再把纹理数据从CPU加载进GPU中进行绘制。而不常用的纹理则使用到时才加载(因为我们项目资源还是很多的)。需要结合项目的实际情况而选择不同的方式。
参考资料链接:https://www.w3cschool.cn/microgame/microgame-7w1536h7.html
7.包体大小优化
1.图片压缩(压缩图片只影响包体大小,不影响加载图片后的内存大小)。可以选择2种或以上的压缩方式(我尝试过6种+的压缩方式,各有特点,有些压缩率比较低,有些压缩得比较厉害但在某些图片上失真相对严重),选1种在手机上看不出不一样的压缩方式(效果较好的)进行主要的压缩,然后一些图片不太明显的或者要求不太高的就用一些压缩更厉害的方式,结合项目的情况加以选择。
2.善用九宫格,可以减少大量背景图资源。
3.音频压缩。(我也不是这方面专业的,只是拿格式工厂进行简单的压缩,测试过选一种音质听着没什么区别的,最终确实可以大幅度减少了音频文件的大小,同时也更省内存和解码资源)。
4.资源分割。比如:首包资源、分包资源、后续包资源。
首包资源:让玩家一开始维持着流畅游戏而无需下载任何东西(例如新手指引内的资源全部是首包资源,玩家玩新手指引时无需再进行任何下载)
分包资源:就是首包资源以外,玩家进行游戏必须的资源呗。可以在游戏内做个按钮,新手指引后给点奖励诱惑玩家下载完剩余的资源。即使玩家没手动下载,玩家在游戏过程中遇到哪个资源缺的也进行动态下载,就是说玩家依然能正常进行游戏,只是需要边玩边下载需要的资源而已。
后续包资源:例如一个推图的功能,根据等级开放后面的图的,完全可以把30级后的资源放到后续包,等他到了等级要打的时候再下载。
按照上述的进行资源分割,一个1G的游戏,可能玩家一开始只需下载100多M的游戏包,就可以在前期比较流畅的进行游戏了。
5.对美术制作的资源进行适当的限制:下面列举部分情况,如:
帧动画:抽帧,抽掉不必要的帧(在不太影响效果的情况下减少帧数),这样既能优化包体大小的同时也能减少内存压力。
骨骼动画:充分利用好骨骼动画的优势,把能通用的动画都单独抽出来,然后通过插槽把各种动画组合在一起,让公用的动画不用多次制作占用空间,如10个人物角色,拿着不同的武器,如果基础人物能通用出来,那就只需要做一个人,10把武器的动画,然后不同角色用同样的人拿上不同的武器就可以了。
8.游戏安全性
1.资源代码安全性,简单来说就是加密咯。自己找个加密的方式,在打包游戏时,对你想要进行加密的一切资源(代码、图片、配置文件等)进行加密,只需要稍微修改一下引擎源码,在读取相关资源时先进行解密即可正常读取了。 加密消耗的是打包的时间,这个不影响玩家没啥关系,但解密确确实实需要消耗玩家的时间和内存,所以具体是否加密,哪些需要加密就根据实际情况选择咯。
2.客户端数据安全性。对于所有客户端上执行的操作,都要做一个判断,这操作最终决定权是否在服务器手上,如果决定权是在客户端手上,那这时候就要小心了,防止玩家通过“八门神器”之类的像金手指一样的软件把客户端数据给改掉,可以简单的做一些数据分拆,位移,计算时再通过函数改回来的手段进行简单的防止作弊。 因为像金手指那种一般玩家都要看到屏幕上显示的数值再进一步进行修改的,只要内部保存的数据和显示的数据不一致他就改不了了。 这里只防止简单的作弊,更复杂的反编译等我们也对源码进行了加密,而且把大量的时间投入到复杂的防作弊上不如放回做好产品上来的好,真要被人花大量时间去破解,证明你的游戏已经很火爆了,到时再花时间花人力去做更安全的代码一切都值得了。
3.发送给服务器的数据安全性:无论是socket连接还是http短连接,对要发送的数据进行一定程序的加密,不要使用明文。如使用protocol buff,虽然它只是一种数据交换的格式,但相对于像XML,JSON格式的数据信息都会明文显示,proto需要双端事先定义好数据的格式文件(.proto 协议文件),然后发送时会转换成二进制流,等服务器端收到信息后再按照协议文件定义的格式,把二进制流转换回对应的数据信息。
9.错误信息收集
定期查看错误日志,极大程度地帮助项目修复那些不为人知的BUG,让项目更加稳定。
1.收集信息的全面性。分别对java or ios,C++,lua(或其他脚本语言),的错误信息进行收集,收集后统一发送到服务器。可以在原生代码上写信息收集的代码,也可以接入第三方的SDK来完成对信息的采集。
2.信息分类。可以把信息分类为info、warning、error。 在正式版上,只输出与收集error信息。而在开发版或者debug版上,可以根据自身需求把info和warning信息都输出出来。因为程序员开发时往往都要看日志进行调试,但这些日志在正式版上没必要输出给玩家看,所以进行信息分类,只收集error信息即可。

10.减轻CPU的负担
1.减少由于大量对象频繁创建销毁而产生的大量IO操作。比较简单有效的方法就是创建一个内存池,从内存池中分配资源。 其次可以结合游戏的实际情况作出一些改动。如:一场战斗中的子弹资源,假设这游戏战斗内无法换枪,那么他进入战斗时子弹的一切东西都是固定了(子弹资源、爆炸特效、音效等),每一发子弹都进行创建与销毁就非常消耗性能了,完全可以创建后不释放,子弹爆炸后下一颗子弹发送时就可以重复利用了,等到战斗结束才一次过释放。
2.尽量避免在类似update这种每帧都会执行的函数里面做大量复杂的工作。主体思路就像我们写openGL时一样,能在初始化完成的东西千万别放在顶点着色器中处理;能在顶点着色器处理的东西,千万别放在片段着色器来计算! 如果一定要放在update里执行,1.考虑是否可以降低频率,不每帧执行改成每秒执行? 2.放在计算效率更高的语言上处理,如你在lua写的程序,复杂的运算能否放到C++上计算? 3.实在没折,就优化算法吧…………

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值