zoukankan      html  css  js  c++  java
  • MMORPG战斗系统随笔(四)、优化客户端游戏性能

       转载请标明出处http://www.cnblogs.com/zblade/

            说到游戏性能,这是一个永恒的话题。在游戏开发的过程中,性能问题一直是我们研发需要关注的一个节点。当然,说句客观话,很多程序员在写代码的时候,是不会过多的在意其代码的性能的。其实这是一种很危险的思想,一个不断进阶的程序员,需要持续的关注自己的代码质量,同时需要借助工具来反查自己编写的代码质量。很多初阶的程序员,在编写代码的时候,是单纯的以完成任务为目标的,其实在写完后,可以没事翻看以前自己的代码,就会有很多不一样的视角。比如,代码的整洁性是否注意,可读性是否有注意,注释是否有相应的标写,这是一个基本的代码规范。

      除了基本代码规范,进一步的需要考虑的是代码的质量,也就是性能是否有消耗,是否写出一些消耗较大的逻辑。对于性能的优化,就需要相应的性能工具来进行计算和统计,不然所谓的性能优化都属于自我安慰,没有数据支持。

      下面我回忆总结一下自己参与的一些性能优化工作,也算一个个人总结吧。

    一、对lua导出表的优化

      这是我最早而且持续时间最长的一个优化工作。在我们的游戏中,策划会有大量的配置表,全都配置在excel表中,通过编写导表工具,可以将excel表导出为对应的lua表,用于在游戏中进行lua表的加载和查找。我总结一下优化的几个节点:

            1、最初版本的导表

            最初版本的lua导表,是将excel中对应的key-value值逐一导出,其基本的格式可以表示为:

    local skill = {
       {Id = 1, CD = 5, SkillTarget =1,...}    
       {Id = 2, CD = 3, SkillTarget =2,...}    
       {Id = 3, CD = 6, SkillTarget =1,...}    
    }

      这种通过在table中插入多个table的方式,每个子table为hash的存储方式。仔细分析一下存储的类型,对于table而言,其基本的存储方式分为hash和array两种方式。hash的存储会有较快的读取操作,但是会带来更多的内存占用,因为其需要申请内存来存储key值,不单单只存储value值。对于array的存储方式,lua在底层的实现的时候,并不是我们常见的采用2的幂次的大小来存储,也就是,不是采用2,4,8,16,32...这种大小的存储,而是实际的根据数组的大小来分配对应的大小数组。这是后面一次优化中,通过实现设置对应大小的数组来分配内存,发现对比并没有对应的优化,后来查看源码才发现其数组的分配规则。

           最初版本的所有lua导出表都采用hash的存储方式,那么可以想象有多大的内存占用~大概在50M左右~这是一个非常可怕的内存占用,如果在游戏加载的过程中,需要加载这么大的配置表,那么游戏加载会有多缓慢,可以体会一下 :D

          2、初次优化的导表

           其实,最开始引起我们关注的并不是其存储方式,而是策划配置的某些excel表过于巨大,某些单一的表就会大到几M的数量级,分析其中的数据,很多数据并不是反复分散变化的,而是集中在较多的几个高频中,这就引申出一种优化的策略:提取高频的配置作为默认配置,少数低频的配置采用对应的配置,这样就可以得到对应的一个内存优化。具体的优化策略可以详见我的这篇文章:table重构index方法优化内存http://www.cnblogs.com/zblade/

           这篇文章的基本思想,就是通过重构和提前高频的方法,缓存高频,每次查找的时候,默认去高频中查找,如果没有,则走自身的查找。可以查看文章中的对比存储方法,写的比较详细。

        采用这种优化方法后,整体的lua导出表得到大大的优化,整体缩减了接近20M左右,可见我们的策划有多喜欢配置同样的配置 Orz

          3、进一步的优化导表

        通过上面的一次优化后,我们大大的优化了游戏的lua表内存占用,整体游戏在加载的时候,lua表统计的内存占用在30M左右。如果我们只满足于这一点,那么就不会有下一步的优化了。后续在第二次的优化上,我还进行了一些特定的优化,但是都没有太过于亮眼的优化,包括前面提到的用array的方式代替hash存储,也研究过设置array的大小,不采用2的幂次大小分配内存,事实的统计显示其实lua本身的内存分配就是采用 按需分配的,不会过多的分配内存。

        最好的优化方法,就是多和别人交流,这是我对自己优化的一个另类总结吧。在上一次的优化后,都没有太大的性能提升,但是这部分的内存占用又一直处于一个比较大的部分,后来在和其他项目组交流的时候,提供了一种静态加载的实现思路。对于lua导出表,可以用一种静态分表加载的方式。特别是对于占用内存比较大的一些表(具体每个表的占用可以做一个内存统计排序),可以分开成多份,这样在最初的游戏加载的时候,加载的是头文件部分,这部分是不包含具体的配置表信息的,具体的配置表信息存放在分表文件夹中。在实际使用的时候,比如将技能表分为part1-20,每个分表大小100,定位要获取id为1001的技能的配置,这时候去加载其对应的分表part11, 加载进来后定位取到1001的技能配置,这样就不会多余的加载part12-20这部分的数据表。

         通过静态分表加载的方式,游戏在最初加载lua表的时候,对于分表实现的lua表大大降低了游戏的内存占用,效率提升非常明显,内存占用缩小到20M左右=。=

           基于这样的实现方式,进一步分析,其实这些lua表并不需要在游戏启动的时候加载,只需要在每次第一次使用相关表的时候加载,再加载进来,同时对于大的内存表进行分块加载,综合这样的实现方式,对lua表的优化最终达到一个可以接受的地步。

    二、3D模型的优化

      在3D游戏中,美术在3DMAX中做完游戏模型的相关建模和动画后,会导入到unity中作为游戏资源。通常,这样的美术资源是含有冗余的资源,可以在unity中对这些冗余的资源进行优化,降低美术资源的内存占用。这儿我例举两种美术资源相关的优化:模型UV的优化和动画animation冗余节点的优化。

      1、模型UV的优化

       一半导入unity的角色模型,都含有冗余的多套UV,对于这些对于的UV,我们是可以用一定的方法来实现剔除的。由于u3d中模型的类型为FBX类型,所遇需要采用FBX SDK,结合FBX SDK的API进行UV的剔除。这儿给出一个FBX SDK的官网:FBX SDK官网

           FBX SDK的官网给出了两种实现方法:C++和python,对应的都有详细的API讲解和例子。我主要采用python的方法,搭建基本的pyhton环境后,就可以查看相关的API接口,从而实现对应的代码编写。具体代码由于保密我就不提供了,我可以给出相关的实现思路:首先是查找到指定文件夹下的所有FBX文件,然后对于这些FBX文件进行读取,获取其下面的所有节点,对于每一个节点,读取其属性,如果属性包含UV,则读取UV的数量,如果数量大于1,则说明有多余的UV,则将其移除,然后保存

      通过移除多余的UV,可以实现对FBX文件的一次"瘦身",从而降低美术资源对游戏内存的占用。如果有什么需要交流的,可以在下面留言进一步讨论。

          2、动画animation冗余节点的优化

           模型的动画,在美术制作完后,会被导入到unity中,进而可以在animation窗口中查看。其实我们分析模型的animation,我们可以发现,大部分节点的animation在插值过程中,其实质是不会变换的,基本为1。由于每个节点的animation主要修改 rotation/scale/position这三个transform属性,所以对于rotation和scale可以进行一次剔除:如果整体animation的插值都为1,则这样的一条animationCurve是可以剔除的。不知道有没有读者也有相关的优化策略,可以在下面留言进一步的讨论。

      我就说说我用FBX SDK优化animation冗余节点的思路:首先获取到FBX模型,然后获取到对应的FbxAnimStack,也就是animationClip,获取到animationClip后,进一步获取到animationLayer,基于animationLayer,可以获取到每个animationLayer的animationChannels,也就是rotation/scale对应的xyz三个通道,分析这三个通道的通道值,如果都在1的误差允许范围附近,则可以移除这个rotation/scale。

          最后存储修改后的FBX文件,可以发现以前较多的animation经过优化后,都被剔除过滤掉了,这说明很多时候我们的animation其实只是在修改position这一个属性,并不会修改rotation/scale这2个属性,这是可以被优化的。

    三、头顶文字描边的优化

      熟悉MMORPG游戏的都知道,在游戏中角色头顶都会有名字,称号等相关的文字信息,实时的反应玩家的信息,便于游戏的交互。在MMO游戏中,会有很多的玩家同时在场,同时也会有较多的怪物NPC等游戏角色的存在,而为了追求游戏体验,都会对玩家头顶的文字进行描边,总结一下对头顶文字描边的优化过程:

      1、4个基准点的描边

      最开始提出要添加头顶文字的描边后,和leader商量了一下,就只是单纯的对玩家头顶文字进行四个点的描边。这里面可能会说一些渲染相关的知识,通常我们在进行渲染描边的时候,我们最开始是需要填入需要渲染的UV三角形的,如果有不是很了解的同学,可以搜查一下渲染的过程,填充完渲染三角形后,CPU才会把这些三角形传递到GPU中,设置渲染状态,从而进行三角形的渲染,得到最后的渲染结果。所以描边要么在CPU中进行,要么在GPU中进行,我选择的是在CPU中进行,在填充三角形的UV的时候,选择扩展这些UV数组。

      基本的实现思想是:原始的UV数组一份,然后分别在左上,右上,左下,右下的四个基准点进行拓展,这样就会得到五个数组,最终描绘出来一个描绘了外轮廓的描边结果,大概结果示意如下图:

                                                               

      2、四方向的描边

      基于四个基准点的UV扩展,是可以实现基本的描边效果,但是仔细推敲就会发现,这其实是对文字的外轮廓进行一个拓展而已,获得的效果其实并不是非常好,如果对描边效果有一定要求,这样的描边并不是很优秀的。果不其然,高层要求优化描边效果。我仔细推想了一下,就把四个基准点的描边变为四个方向的描边,分别为向左,向右,向上,向下的UV扩展,如果用一个文字来表示效果,可以表示为:

                                                                                                                  

      读到这儿,我想你也可以自己在头脑中描绘出这样的5份UV,一份为原始的UV,一份为向左偏移一定offset的UV,一份为向右偏移一定offset的UV,一份为向上偏移一定offset的UV,一份为向下偏移一定offset的UV,这5份UV在进过三角形设置后,会传递到GPU中进行三角形描绘,得到的结果就会是比较理想的描边结果,分别描绘了文字的上下左右四个方向。

      这种描边方式有很好的描边效果,带来的消耗,也是可以直接理解的,通过四份UV的复制,而不是简单的四个点的复制,对于内存的占用会更大。可见想要有好的效果,还是需要一定的付出。最后这种描边也被优化掉了,最后采用的是shadow来代替描边,通过动态合批的处理来降低内存占用。不过从这次描边的优化,可以更了解整个渲染的过程,CPU是如何计算UV的,怎么设置UV,最后在GPU上渲染得到对应的效果,都是一个比较直观的过程。

     四、代码的优化

       写到这儿,也提一下对代码质量的优化吧。现在比较主流的热更都会采用lua来实现逻辑,lua这种脚本语言,上手极其容易,但是如果使用不是很仔细,还是会带来一些不必要的问题。table作为Lua的基本构造点,在使用的时候,会有一些可以规避的地方。比如在频繁更新或者使用的代码部分,不要反复申请table,这会使得虚拟机不断的去进行内存分配。我们可以将这些频繁使用的功能相同的table作为一个内部变量存储,在第一次进行申请,当前更新后,将其内部的值赋值为nil,这样下次再使用的时候,是在当前table的基础上进行扩展,这样采取扩展table而不是频繁构建新table的方式,可以避免内存碎片的产生。

      此外在检测代码质量的时候,最好做好相关的工具,进行各种性能统计,我们才能得到实际的性能数据,通过性能数据的分析修改可能存在的问题点。比如对于高频变化的数据,是否采用增删改的方式更能提升性能,对于高频检测的方法,是否通过特殊的规则可以降低检测的次数等等,这些都需要结合实际的应用设计来修改。

      当然,游戏还包含一部分的优化,就是UI部分的优化,这部分可以参考MMO雨松的博客,他主要负责这部分的工作,所以可以参考他的博客,不知道最近他有没有更新博客,哈哈,估计太累了~

           好了,本文也算一个小结,后续我看还有什么需要继续写的,我会再接着写博客,下篇文章见~

  • 相关阅读:
    sqlhelper使用指南
    大三学长带我学习JAVA。作业1. 第1讲.Java.SE入门、JDK的下载与安装、第一个Java程序、Java程序的编译与执行 大三学长带我学习JAVA。作业1.
    pku1201 Intervals
    hdu 1364 king
    pku 3268 Silver Cow Party
    pku 3169 Layout
    hdu 2680 Choose the best route
    hdu 2983
    pku 1716 Integer Intervals
    pku 2387 Til the Cows Come Home
  • 原文地址:https://www.cnblogs.com/zblade/p/7647080.html
Copyright © 2011-2022 走看看