集合代理组件用于基于集合文件内容动态加载卸载新的游戏 “世界”. 可以用来实现切换关卡, GUI 屏幕, 在关卡里加载卸载插播 “场景”, 加载卸载迷你游戏等等功能.
Defold 把所有游戏对象组织在集合里. 集合可以包含游戏对象和其他集合 (即子集合). 集合代理可以让你把内容拆分到各个集合然后用脚本动态加载卸载这些集合.
集合代理不像 集合工厂组件. 集合工厂用于在当前游戏世界创建集合. 集合代理用于运行时创建全新游戏世界, 它们用处不同.
把一个集合代理组件加入到游戏对象上 右键点击 并从上下文菜单中选择 Add Component ▸ Collection Proxy.
设置 Collection 属性来引用一个集合, 就是你希望动态加载进运行环境的集合. 这些引用是静态的, 所以确保把游戏所需的各个部分都通过集合引用到.
(也可以编译时排除一部分内容需要的时候用代码下载而不使用 Exclude 选项和 热更新功能.)
当 Defold 引擎开始工作最先把 启动集合 导入运行环境并对其中的所有游戏对象进行初始化. 然后开启游戏对象和它们的组件. 在 项目配置 里设置把哪个集合作为启动集合使用. 依照惯例启动集合都叫做 “main.collection”.
启动集合实例化时引擎会为 “游戏世界” 里的游戏对象和组件分配足够的内存空间. 对于物理模拟和碰撞对象, 引擎会为其建立另一个游戏世界.
因为脚本要能定位任何地方的游戏对象, 包括启动集合之外的集合里的对象, 所以集合必须有独立的属性: Name:
如果被加载集合里还有集合代理, 代理引用的集合 不会 被自动加载. 需要手动写代码进行加载.
通过代理动态载入集合需要用脚本给代理发送 "load"
消息:
-- 让代理 "myproxy" 开始加载集合.
msg.post("#myproxy", "load")
集合代理会告诉引擎需要为新游戏世界分配多大空间内存. 另一个物理世界也被建立起来连同集合 “mylevel.collection” 里的游戏对象都会被实例化.
新游戏世界的创建通过 Name 属性引用的集合文件为蓝图, 本例中是 “mylevel”. 不能有重名. 如果集合文件 Name 重名, 引擎会报错:
ERROR:GAMEOBJECT: The collection 'default' could not be created since there is already a socket with the same name.
WARNING:RESOURCE: Unable to create resource: build/default/mylevel.collectionc
ERROR:GAMESYS: The collection /mylevel.collectionc could not be loaded.
当集合加载完毕, 集合代理会向发送 "load"
消息的脚本发回 "proxy_loaded"
消息. 收到此消息就可以进行集合初始化等工作了:
function on_message(self, message_id, message, sender)
if message_id == hash("proxy_loaded") then
-- 新集合已加载完毕. 初始化并激活它.
msg.post(sender, "init")
msg.post(sender, "enable")
...
end
end
"load"
"proxy_loaded"
消息."async_load"
"proxy_loaded"
消息."init"
init()
函数会被调用."enable"
使用集合文件的 Name 属性用来定位其中的游戏对象和组件. 比如启动集合里有个加载器对象, 一关结束后让它加载下一关:
-- 告诉加载器加载下一关:
msg.post("main:/loader#script", "load_level", { level_id = 2 })
卸载需要发送的消息和加载相反:
-- 卸载当前关卡
msg.post("#myproxy", "disable")
msg.post("#myproxy", "final")
msg.post("#myproxy", "unload")
"disable"
"final"
final()
函数会被调用."unload"
如果不那么细致, 只发送 "unload"
消息就好. 在卸载前代理会自动进行关闭和析构工作.
当即和卸载完毕, 集合代理会向发送 "unload"
消息的脚本发回 "proxy_unloaded"
消息:
function on_message(self, message_id, message, sender)
if message_id == hash("proxy_unloaded") then
-- Ok, 游戏世界卸载完成...
...
end
end
集合代理的更新周期可以使用 time step 进行缩放. 也就是说即使游戏是 60 FPS 的, 集合代理游戏世界的速度还是可以变得可以更快或者更慢, 收以下几方面影响:
update()
函数里的 dt
还可以设置刷新执行模式, 可以控制游戏刷新是分散的(速度缩放小于1.0有效) 还是连续的.
通过发送 set_time_step
消息给集合代理组件来设置时间步缩放系数与执行模式:
-- 把加载的游戏世界时间放慢为1/5.
msg.post("#myproxy", "set_time_step", {factor = 0.2, mode = 1}
这样做的结果, 我们可以通过一段代码来进行观察:
function update(self, dt)
print("update() with timestep (dt) " .. dt)
end
时间步系数为 0.2, 控制台打印如下输出:
INFO:DLIB: SSDP started (ssdp://192.168.0.102:54967, http://0.0.0.0:62162)
INFO:ENGINE: Defold Engine 1.2.37 (6b3ae27)
INFO:ENGINE: Loading data from: build/default
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0.016666667535901
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0.016666667535901
update()
仍然是每秒调用 60 次, 但是 dt
值变了. 可以看到只有 1/5 (0.2) 的 update()
调用包含 1/60 秒的 dt
参数, 其他都是 0. 物理模拟也基于 dt 每 5 帧步进一次.
可以使用集合的时间步功能来暂停游戏, 例如弹出窗口或者游戏窗口失去焦点时, 使用 msg.post("#myproxy", "set_time_step", {factor = 0, mode = 0})
暂停游戏, 然后使用 msg.post("#myproxy", "set_time_step", {factor = 1, mode = 1})
继续游戏.
详情请见 set_time_step
.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB