Read this manual in English

Defold 游戏优化

开发时就要考虑游戏在目标平台上的运行和优化. 需要考虑以下几个方面:

  • 应用大小
  • 运行速度
  • 内存占用
  • 耗电情况

应用大小优化

Defold 编译和打包时建立了一个依赖树. 编译系统从 game.project 文件指定的启动集合开始检查每个被引用的集合, 游戏对象及组件需要的资源. 这些被依赖的资源才会最终被导入包内. 没被引用的被排除在包外. 即使这样开发者还应该考虑包内资源空间占用情况. 有些平台与发布渠道限制了应用包体大小:

  • Apple 和 Google 规定了设备使用移动网络 (而不是 Wifi) 下载应用的大小限制.
    • 2019 年夏 Google Play 限制 100 MB, Apple App Store 限制 150 MB.
  • Facebook 建议 Facebook Instant Game 要在 5 秒内最好是 3 秒内启动.
    • 这虽然没有直接规定大小, 我们讨论认为大小应该限制在 20 MB 以内.
  • Playable ads 基于广告商网络一般限制在 2 到 5 MB.

:::注意 根据 2017 年一项研究表明 “APK 文件大小每增加 6 MB, 安装率就会相应降低 1%.” (source) :::

为了更好的分析包体空间占用可以在编译时 生成编译报告. 通常声音和图片占游戏空间最大部分.

缩减引擎

可以使用 application manifest 文件 去掉引擎中不需要的功能. 比如游戏不用物理效果就去掉物理引擎.

声音优化

Defold 支持 .ogg 和 .wav 文件其中 .ogg 一般用于音乐 .wav 一般用于音效. Sounds 必须是 16-bit 采样率 44100 所以编码前就要对其做好优化. 可以使用第三方软件降低音质或者把 .wav 转换成 .ogg.

图片优化

对游戏的图片优化有几种办法, 首先要做的就是检查图集和瓷砖图源内容图片的大小. 导入的图片尺寸不要超过游戏所需大小. 导入大图片再缩小使用是现存资源的浪费. 可以使用第三方图片编辑器先把图片修改成所需大小. 对于背景图之类的有时可以导入小图片再放大使用. 小图改好了还要考虑图集和瓷砖图源本身的大小. 不同平台和显示硬件的纹理尺寸限制可能不一样.

这个帖子 在使用脚本与第三方软件修改图片大小的方面给出了许多建议.

  • HTML5 游戏最大纹理: https://webglstats.com/webgl/parameter/MAX_TEXTURE_SIZE
  • iOS 游戏最大纹理:
    • iPad: 2048x2048
    • iPhone 4: 2048x2048
    • iPad 2, 3, Mini, Air, Pro: 4096x4096
    • iPhone 4s, 5, 6+, 6s: 4096x4096
  • Android 最大纹理尺寸各不相同但是新设备基本都支持 4096x4096.

如果图集太大就需要切分成小图集或者使用 texture profile 缩小整个图集. Defold 的 texture profile 系统不但可以缩小图集还可以通过应用压缩算法减小图集占用空间. 详见 纹理档教程.

  • mipmaps: false
  • premultiply_alpha: true
  • format: TEXTURE_FORMAT_RGBA
  • compression_level: NORMAL
  • compression_type: COMPRESSION_TYPE_BASIS_UASTC

优化和管理纹理可以参考 这个帖子.

优化字体

如果在 Extra Characters 里指定文字而不是勾选包含所有字符, 字体大小就会减小.

排除内容按需下载

另一个减小包体的办法是打包时把部分内容排除在外, 需要时再下载. 一开始被排除的东西可以是锁住的关卡, 未激活的角色, 皮肤, 武器或者是车辆. Defold 提供了叫做热更新的按需下载内容的方案. 详情请见 热更新教程.

Android 大小优化

Android 包必须支持 32-bit 和 64-bit CPU 架构. 当 打包 Android 时你可以指定包含哪种 CPU 架构:

Signing Android bundle

Google Play 支持同一个游戏的 多 APKs 包, 所以你可以用生成连个 APKs 的方法减小包体, 每种 CPU 架构生成一个, 然后把这两个都上传至 Google Play.

还有一种方法是结合 APK 扩展文件热更新内容. 谢谢 资源大厅的 APKX 扩展.

应用运行速度优化

你要知道游戏运行效率瓶颈在哪才能进行优化. 每帧的什么操作最耗时间? 与渲染有关吗? 与游戏逻辑有关吗? 与场景图有关吗? 建议使用内置的分析工具分析这些事情. 使用 屏幕或者网页分析器 对游戏进行采样再分析哪里应该优化. 发现最耗时的操作就找到了优化方向.

减少脚本运行时间

如果分析工具指出 Script 部分耗时太多. 当然每帧运行脚本越少越好. 要是 update()on_input() 里面代码太多很可能影响每帧运行性能, 尤其是对于低端设备. 一些建议是:

代码有效率

用回调别用轮询. 引擎提供的功能别自己手动实现 (比如用 go.animate 别用手动实现动画等等).

减少垃圾回收

每帧创建大量临时 Lua 表之类的对象会激发 Lua 的垃圾回收机制. 垃圾回收可能会造成卡一下. 尽可能重用表别在循环里创建很多表.

预哈希消息与行为id

如果要处理很多消息处理很多输入的话推荐把字符串提前哈希保存. 比如如下代码:

function on_message(self, message_id, message, sender)
    if message_id == hash("message1") then
        msg.post(sender, hash("message3"))
    elseif message_id == hash("message2") then
        msg.post(sender, hash("message4"))
    end
end

这样每次接收消息都要调用很多哈希函数. 预先把哈希字符串保存起来就可以提高效率:

local MESSAGE1 = hash("message1")
local MESSAGE2 = hash("message2")
local MESSAGE3 = hash("message3")
local MESSAGE4 = hash("message4")

function on_message(self, message_id, message, sender)
    if message_id == MESSAGE1 then
        msg.post(sender, MESSAGE3)
    elseif message_id == MESSAGE2 then
        msg.post(sender, MESSAGE4)
    end
end

Prefer and cache URLs

定位一个游戏对象和组件可以使用它的 id 的字符串或者哈希或者 URL. 字符串和哈希在内部被转换成 URL. 对于经常要使用的 URL 建议预先保存, 利于提高性能. 如下例:

    local pos = go.get_position("enemy")
    local pos = go.get_position(hash("enemy"))
    local pos = go.get_position(msg.url("enemy"))
    -- 处理位置变量

以上三个定位 id 都是 enemy. 第一第二行 id (字符串或哈希) 使用前会转换为 URL. 所以预先保存 URL 在需要时使用就能提高效率:

    function init(self)
        self.enemy_url = msg.url("enemy")
    end

    function update(self, dt)
        local pos = go.get_position(self.enemy_url)
        -- 处理位置变量
    end

减少渲染一帧的时间

分析工具可以在 RenderRender Script 部分指明哪些渲染耗时较长. 考虑以下方面来改善渲染耗时:

  • 减少 draw calls - 减少 draw call 可以参考 这个帖子
  • 减少 overdraw
  • 减少 着色器复杂度 - 对于 GLSL 优化可以参考 Kronos 的这个文章. 还可以修改 Defold 的默认着色器 (位于 builtins/materials) 或者针对低端设备降低着色器精确度. 所有着色器都使用 highp 精确度, 如果改成 mediump 可能会提升一些性能.

降低场景图复杂度

如果分析器指出 GameObject 部分, 尤其是 UpdateTransform 取样耗时较高就需要进行一定的优化. 方法如下:

  • 剔除 - 如果游戏对象不可见, 关闭游戏对象 (及其组件). 基于游戏类型采取不同方法. 对于 2D 游戏视口内看不到的东西都可以关闭. 可以使用物理 trigger 进行检测或者把所有东西分为若干组群分别检测. 碰到需要关闭或者开启的情况就向游戏对象发送 disable 或者 enable 消息即可.

优化内存使用

此部分教程未完成. 讨论涵盖以下方面:

微调组件计数

检查 game.project 里的组件计数. 使用 调试器 获取准确的组件和资源使用情况, 并将游戏配置为使用更接近组件和资源实际计数的最大值. 这将减少游戏使用内存量 (参考组件 最大数优化).

Heap 大小 (仅 HTML5)

确定给游戏设置最小的 heap 尺寸. 打开游戏玩最多 “资源展示” 的一关. 开启浏览器的开发者工具, 在控制台上写入 HEAP8.length / 1024 / 1024. 用这个数据加上 10-15% 来 game.project 里设置 Heap 大小.

优化耗电

此部分教程未完成. 讨论涵盖以下方面: