转载请标明出处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雨松的博客,他主要负责这部分的工作,所以可以参考他的博客,不知道最近他有没有更新博客,哈哈,估计太累了~

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

MMORPG战斗系统随笔(四)、优化客户端游戏性能的更多相关文章

  1. MMORPG战斗系统随笔(一)

    前言 很久没有更新博客,中间迁移过一次博客,后来一直忙于项目的开发,忙的晚上回去没时间写博客,周日又要自我调整一下,所以空闲了很久没有继续写博客.最近终于慢慢放慢节奏,项目也快上线了,可以有空写一些个 ...

  2. MMORPG战斗系统随笔(一)、战斗系统流程简介

    前言 转载请标明出处http://www.cnblogs.com/zblade/ 很久没有更新博客,中间迁移过一次博客,后来一直忙于项目的开发,忙的晚上回去没时间写博客,周日又要自我调整一下,所以空闲 ...

  3. MMORPG战斗系统随笔(三)、AI系统简介

    在设计一款游戏的时候,如果我们是玩家,是希望自己能够操作角色畅玩游戏的.在一款MMORPG游戏中,大部分的实际游戏角色,是需要玩家来操作的,通过在游戏大世界相互完成游戏中的任务等等来体验游戏.在大世界 ...

  4. MMORPG战斗系统随笔(二)、浅谈场寻路Flow Field PathFinding算法

    转载请标明出处http://www.cnblogs.com/zblade/ 今天给大家带来一篇游戏中寻路算法的博客.去年,我加入一款RTS的游戏项目,负责开发其中的战斗系统,战斗系统的相关知识,属于游 ...

  5. 客户端JS性能的一些优化的小技巧

    下面是一些关于客户端JS性能的一些优化的小技巧:1.[顶]关于JS的循环,循环是一种常用的流程控制.JS提供了三种循环:for(;;). while().for(in).在这三种循环中 for(in) ...

  6. 如何快速优化手游性能问题?从UGUI优化说起

    WeTest 导读   本文作者从自身多年的Unity项目UI开发及优化的经验出发,从UGUI,CPU,GPU以及unity特有资源等几个维度,介绍了unity手游性能优化的一些方法.   在之前的文 ...

  7. JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能

    摘要: 理解浏览器渲染. 原文:JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这是专门探索 J ...

  8. java gc的工作原理、如何优化GC的性能、如何和GC进行有效的交互

    java gc的工作原理.如何优化GC的性能.如何和GC进行有效的交互 一个优秀的Java 程序员必须了解GC 的工作原理.如何优化GC的性能.如何和GC进行有效的交互,因为有一些应用程序对性能要求较 ...

  9. Silverlight游戏设计(Game Design):(十四)练习用游戏素材资源的获取及相关工具使用心得 --转

    Silverlight游戏设计(Game Design):(十四)练习用游戏素材资源的获取及相关工具使用心得 通过前6节的Demo制作演示,大家应该已经相当熟悉这款Silverlight-2D游戏场景 ...

随机推荐

  1. HTML <area><map>标签及在实际开发中的应用

    之前,我一直以为HTML <area>是一个鸡肋HTML,估计到了HTML5时代会被废弃的命.但是,最近一查资料,乖乖了个咚,不仅没被废弃,反而发展了,新增了一些标签属性,例如rel,me ...

  2. UWP 手绘视频创作工具技术分享系列 - SVG 的解析和绘制

    本篇作为技术分享系列的第一篇,详细讲一下 SVG 的解析和绘制,这部分功能的研究和最终实现由团队的 @黄超超 同学负责,感谢提供技术文档和支持. 首先我们来看一下 SVG 的文件结构和组成 SVG ( ...

  3. Apple公司开发者账号申请(2017包含邓白氏码申请)

    1.首先看需要那种账号 2.这个需要的是公司开发者账号,首先我们注册一个普通apple账号 打开网址 https://developer.apple.com 进入点击Account 进入登录页面,点击 ...

  4. Java虚拟机JVM内存分区及代码执行机制

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt230 1.  JVM体系结构 图1 JVM体系结构    方法区:存放JVM ...

  5. visual studio xamarin 离线安装文件以及 android 模拟器

    介绍 为了使用vs开发android我也是煞费苦心,先是从网上各种搜刮文章,然后找各种各样的离线包(因为国内网络是下载不了C#/Xamain)的包的,还有各种各样的安装包,都已快接近奔溃的边缘.每次不 ...

  6. Vuforia开发完全指南---Vuforia概述

    Vuforia概述 AR(Augmented Reality)增强现实,想必大家都已经很熟悉了.这是当下最热的技术之一,是利用计算机视觉和计算机图像学领域的相关知识将虚拟世界融入到现实生活当中.AR和 ...

  7. 联合线程(案例顾客买蛋糕 :使用join()方法)

    运行效果图:

  8. 团队作业4——第一次项目冲刺 FiRsT DaY

    项目冲刺--first blood 今天是阳光明媚的一天[明明是阴天好吗= =],今天是心情愉悦的一天[每天都要提交博客高兴个水水哦-3-] 天霸动霸.tua小队迎来了第一敏捷冲刺,小伙伴们是时候打起 ...

  9. 201521123066《Java程序设计》第五周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪 ...

  10. 201521123118《java程序与设计》第9周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 1. 常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出 ...