找回密码
 立即注册
首页 业界区 安全 《影子冒险》—— 性能优化篇

《影子冒险》—— 性能优化篇

孜尊 6 天前
前言

这篇文章记录一下,我独立开发微信小游戏的过程中遇到的性能优化问题,以及对应的优化策略。如果有帮助到大家,可以来体验看看我的小游戏哦,可以扫二维码,或者在微信上搜索《影子冒险》哦。
1.png

对象池优化

在项目前期没有做对象池优化,对于对象都是生成用完后即销毁,加入对象池,避免频繁的性能消耗。
  1. ---@generic T
  2. ---@class ObjectPool:T
  3. local ObjectPool = class("ObjectPool")
  4. function ObjectPool:ctor(createFunc, getFunc, recycleFunc)
  5.     self.createFunc = createFunc
  6.     self.getFunc = getFunc
  7.     self.recycleFunc = recycleFunc
  8.     self.pool = {}
  9. end
  10. function ObjectPool:Get()
  11.     local obj = nil
  12.     if #self.pool > 0 then
  13.         obj = table.remove(self.pool)
  14.     else
  15.         if self.createFunc then
  16.             obj = self.createFunc()
  17.         end
  18.     end
  19.     if obj then
  20.         self.getFunc(obj)
  21.         return obj
  22.     end
  23. end
  24. function ObjectPool:Recycle(obj)
  25.     self.recycleFunc(obj)
  26.     table.insert(self.pool, obj)
  27. end
  28. return ObjectPool
复制代码
Lua层避免每帧调用UnityApi

在Lua层,我有段代码会每帧对当前场景中的所有车辆位置做检查,判断是否能够销毁,其中有段代码会每帧生成一个新表,来存储当前所有的汽车对象。
2.png

可以看到后果,导致Lua内存急速飙升,并造成严重的GC:
3.gif

处理方法,不再生成临时对象来存储车辆,通过倒序的方法来移除车辆:
4.png

但验证后,效果还是不太理想,内存仍然飙升,打开Unity内置的Profiler,可以看到,真正的罪魁祸首是我每帧调用了Unity层的bound做边界检测导致性能问题:
5.png

那我们接下来在Unity层封装下检测方法,避免Lua层疯狂调用Unity层的属性和方法:
6.png

Unity原生的Profiler,可以看到GC大大减少了:
7.png

LuaProfiler也可以看到内存没有飙升这么快了:
8.gif

但这还不够,因为这个检查不需要这么敏感的要每帧去做检查,我们可以优化下,3秒检查一次,最终效果:
9.gif

Lua避免频繁生成对象

对于事件之类的自定义的对象,也要做到尽可能的复用,例如改造前:
10.png

改造后:
11.png

Lua避免重复创建列表,新增一个clear的方法

12.png

UI刷新不要太依赖Update

UI刷新不要太依赖Update,否则带来的性能消耗超出你的想象,请看Profiler:
13.png

14.png

我的解决思路:在lua层提前封装一层Unity层的代码,将各种数据提前缓存好,避免频繁调用Unity层Api,例如Text组件,在lua层封装好当前的Text组件的字符串,在lua层检查到“修改字符串”和“当前字符串”不同,才去调用Unity层Api修改。
但我这里没有提前包,现在项目内已经到处引用着Unity层的代码了,改起来真是痛苦又操蛋!只能先暂时优化掉性能敏感的UI界面了。
在C#层检查Lua内存

可以使用collectgarbage("count")每帧输出lua内存情况,但这个输出不能放到lua层,而是放到C#层,避免影响到lua内存检测:
lua代码:
15.png

C#代码:
16.png

Lua层尽量不要在Update时频繁生成新对象

因为Lua层没有struct结构体,表生成后直接放到堆上,要是在lua层通过Update修改go位置,你就等着看内存飙升吧:
例如我这段代码:
17.png

每帧生成Vector2对象,然后查看内存情况:
18.gif

改造后,lua层:
19.png

C#层:
20.png

然后有点要注意,C#层注入值类型到lua层是不缓存的(为啥不缓存,我有文章分析过了,这里不再解释,请看《tolua源码之小白剖析》系列),例如在lua层获得C#的Vector3类型,每次获得都会在lua层生成一份新的Vector3表,我这句代码就犯了这种错误,这也是导致lua内存飙升的一个罪魁祸首。
21.png

同样要将检查丢给C#层去处理。
然后同样不能在性能敏感的地方使用匿名函数,因为每次调用都会生成一次:
22.png

所以大家一定要注意,性能敏感的地方直接丢对象到lua层吧,不要丢结构体到lua层,对于lua没有结构体的概念,来自C#层的对象都是userdata,我这里又犯了这个错:
23.png

传递值类型RaycastHit到lua层,也会导致性能飙升。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册