开发时就要考虑游戏在目标平台上的运行和优化. 需要考虑以下几个方面:
Defold 编译和打包时建立了一个依赖树. 编译系统从 game.project 文件指定的启动集合开始检查每个被引用的集合, 游戏对象及组件需要的资源. 这些被依赖的资源才会最终被导入包内. 没被引用的被排除在包外. 即使这样开发者还应该考虑包内资源空间占用情况. 有些平台与发布渠道限制了应用包体大小:
:::注意 根据 2017 年一项研究表明 “APK 文件大小每增加 6 MB, 安装率就会相应降低 1%.” (source) :::
为了更好的分析包体空间占用可以在编译时 生成编译报告. 通常声音和图片占游戏空间最大部分.
可以使用 application manifest 文件 去掉引擎中不需要的功能. 比如游戏不用物理效果就去掉物理引擎.
Defold 支持 .ogg 和 .wav 文件其中 .ogg 一般用于音乐 .wav 一般用于音效. Sounds 必须是 16-bit 采样率 44100 所以编码前就要对其做好优化. 可以使用第三方软件降低音质或者把 .wav 转换成 .ogg.
对游戏的图片优化有几种办法, 首先要做的就是检查图集和瓷砖图源内容图片的大小. 导入的图片尺寸不要超过游戏所需大小. 导入大图片再缩小使用是现存资源的浪费. 可以使用第三方图片编辑器先把图片修改成所需大小. 对于背景图之类的有时可以导入小图片再放大使用. 小图改好了还要考虑图集和瓷砖图源本身的大小. 不同平台和显示硬件的纹理尺寸限制可能不一样.
这个帖子 在使用脚本与第三方软件修改图片大小的方面给出了许多建议.
如果图集太大就需要切分成小图集或者使用 texture profile 缩小整个图集. Defold 的 texture profile 系统不但可以缩小图集还可以通过应用压缩算法减小图集占用空间. 详见 纹理档教程.
优化和管理纹理可以参考 这个帖子.
如果在 Extra Characters 里指定文字而不是勾选包含所有字符, 字体大小就会减小.
另一个减小包体的办法是打包时把部分内容排除在外, 需要时再下载. 一开始被排除的东西可以是锁住的关卡, 未激活的角色, 皮肤, 武器或者是车辆. Defold 提供了叫做热更新的按需下载内容的方案. 详情请见 热更新教程.
Android 包必须支持 32-bit 和 64-bit CPU 架构. 当 打包 Android 时你可以指定包含哪种 CPU 架构:
Google Play 支持同一个游戏的 多 APKs 包, 所以你可以用生成连个 APKs 的方法减小包体, 每种 CPU 架构生成一个, 然后把这两个都上传至 Google Play.
还有一种方法是结合 APK 扩展文件 和 热更新内容. 谢谢 资源大厅的 APKX 扩展.
你要知道游戏运行效率瓶颈在哪才能进行优化. 每帧的什么操作最耗时间? 与渲染有关吗? 与游戏逻辑有关吗? 与场景图有关吗? 建议使用内置的分析工具分析这些事情. 使用 屏幕或者网页分析器 对游戏进行采样再分析哪里应该优化. 发现最耗时的操作就找到了优化方向.
如果分析工具指出 Script
部分耗时太多. 当然每帧运行脚本越少越好. 要是 update()
和 on_input()
里面代码太多很可能影响每帧运行性能, 尤其是对于低端设备. 一些建议是:
用回调别用轮询. 引擎提供的功能别自己手动实现 (比如用 go.animate 别用手动实现动画等等).
每帧创建大量临时 Lua 表之类的对象会激发 Lua 的垃圾回收机制. 垃圾回收可能会造成卡一下. 尽可能重用表别在循环里创建很多表.
如果要处理很多消息处理很多输入的话推荐把字符串提前哈希保存. 比如如下代码:
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
定位一个游戏对象和组件可以使用它的 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
分析工具可以在 Render
和 Render Script
部分指明哪些渲染耗时较长. 考虑以下方面来改善渲染耗时:
builtins/materials
) 或者针对低端设备降低着色器精确度. 所有着色器都使用 highp
精确度, 如果改成 mediump
可能会提升一些性能.如果分析器指出 GameObject
部分, 尤其是 UpdateTransform
取样耗时较高就需要进行一定的优化. 方法如下:
disable
或者 enable
消息即可.此部分教程未完成. 讨论涵盖以下方面:
检查 game.project
里的组件计数. 使用 调试器 获取准确的组件和资源使用情况, 并将游戏配置为使用更接近组件和资源实际计数的最大值. 这将减少游戏使用内存量 (参考组件 最大数优化).
确定给游戏设置最小的 heap 尺寸. 打开游戏玩最多 “资源展示” 的一关. 开启浏览器的开发者工具, 在控制台上写入 HEAP8.length / 1024 / 1024
. 用这个数据加上 10-15% 来 game.project
里设置 Heap 大小.
此部分教程未完成. 讨论涵盖以下方面:
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB