Lua 模块使你的项目更具结构化还可以创建可重用库代码. 这是降低项目复杂度的好办法. Defold 可以使用 Lua 模块功能把脚本引入到其他脚本中去. 它可以封装函数 (和数据) 到专门的文件以便由游戏对象和 GUI 脚本重用.
Lua 代码保存在游戏项目中的 “.lua” 文件里, 可以由脚本和gui脚本引入. 创建Lua模块, 在 Assets 视图右键点击, 选择 New... ▸ Lua Module. 输入文件名点击 Ok:
假设 “main/anim.lua” 有如下代码:
function direction_animation(direction, char)
local d = ""
if direction.x > 0 then
d = "right"
elseif direction.x < 0 then
d = "left"
elseif direction.y > 0 then
d = "up"
elseif direction.y < 0 then
d = "down"
end
return hash(char .. "-" .. d)
end
这样其他脚本就能引入这个文件使用其中的函数:
require "main.anim"
function update(self, dt)
-- 更新位置, 设置方向之类的
...
-- 设置方向动画
local anim = direction_animation(self.dir, "player")
if anim ~= self.current_anim then
msg.post("#sprite", "play_animation", { id = anim })
self.current_anim = anim
end
end
关键字 require
引入了模块. 先从 package.loaded
表中查找模块是否已被加载. 找到后, require
返回 package.loaded[module_name]
的值. 否则, 使用加载其加载并处理模块文件.
require
接文件名的语法有点特别. Lua 把文件名中的 ‘.’ 替换为路径分隔符: 在 macOS 和 Linux 上是 ‘/’ , 在 Windows 上是 ‘\’ .
注意尽量不要像上例那样在全局范围保存数据和定义函数. 这样可能会造成命名冲突, 暴露模块数据或者增加模块调用者间的耦合.
Lua 使用 模块 封装数据和函数. Lua 模块是用来保存函数和数据的普通表. 这个表被定义在本地而不是全局范围:
local M = {}
-- 私有
local message = "Hello world!"
function M.hello()
print(message)
end
return M
这样就定义好了模块. 使用时也是, 最好把模块定义为本地变量:
local m = require "mymodule"
m.hello() --> "Hello world!"
假设有如下模块:
-- module.lua
local M = {} -- 本地表
M.value = 4711
return M
然后使用这个模块:
local m = require "module"
print(m.value) --> "4711" (如果 "module.lua" 更改了此值并且完成热重载这个值仍然不变)
如果 “module.lua” 更改了此值并且完成热重载 m.value
仍然不变. 为什么呢?
首先, “module.lua” 表建立在本地环境下作为 引用 返回. 重载 “module.lua” 模块时解析了代码但是新建了另一个本地表 m
却没有更新对它的引用.
其次, Lua 加载了模块. 文件第一次被加载时, 会被放入 package.loaded
表中便于后续快速访问. 如果把模块设置为 nil 可以强制它重新加载: package.loaded["my_module"] = nil
.
要想正确热重载模块, 需要先加载模块, 重置缓存然后更新每个引用模块的文件. 这不利于优化.
一定需要的话可以考虑在 开发时 使用一个解决方法: 把模块表放入全局空间然后返回 M
的引用. 重载时会更新全局表:
--- module.lua
-- 测试完了还是替换成 local M = {}
uniquevariable12345 = uniquevariable12345 or {}
local M = uniquevariable12345
M.value = 4711
return M
带缓存模块的数据表在所有调用者之间共享:
local M = {}
-- 所有调用者共享此表
local state = {}
function M.do_something(foobar)
table.insert(state, foobar)
end
return M
无缓存模块内部不存储缓存数据. 但是它可以把实例缓存作为本地表暴露给调用者. 有若干种方式实现这样的功能:
local M = {}
function M.alter_state(the_state, v)
the_state.value = the_state.value + v
end
function M.get_state(the_state)
return the_state.value
end
function M.new(v)
local state = {
value = v
}
return state
end
return M
调用如下:
local m = require "main.mymodule"
local my_state = m.new(42)
m.alter_state(my_state, 1)
print(m.get_state(my_state)) --> 43
local M = {}
function M:alter_state(v)
-- 使用 : 声明函数会自动添加第一个参数self
self.value = self.value + v
end
function M:get_state()
return self.value
end
function M.new(v)
local state = {
value = v
}
return setmetatable(state, { __index = M })
end
return M
调用如下:
local m = require "main.mymodule"
local my_state = m.new(42)
my_state:alter_state(1) -- 使用 : 调用函数会自动添加第一个参数"my_state"
print(my_state:get_state()) --> 43
__index
查找函数, 但是每个闭包都包含全套功能所以占用内存稍多. local M = {}
function M.new(v)
local state = {
value = v
}
state.alter_state = function(v)
state.value = state.value + v
end
state.get_state = function()
return state.value
end
return state
end
return M
调用如下:
local m = require "main.mymodule"
local my_state = m.new(42)
my_state.alter_state(1)
print(my_state.get_state())
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB