Lua 5.3 中每个脚本拥有唯一的环境
我想能够拥有一段 Lua 代码(一个“脚本”),该代码可以在游戏中的敌人类型之间共享,但是每个脚本实例都有一个唯一的执行环境。为了说明我的问题,这是我第一次尝试脚本的样子:
时间自上次射击以来 = 0
tick = function(entity_id, dt)
时间自上次射击以来 = 时间自上次射击以来 + dt
如果时间自上次射击以来大于10 then
enemy = find_closest_enemy(entity_id)
shoot(entity_id, enemy)
时间自上次射击以来 = 0
end
end
但是这会导致失败,因为我会将全局的时间_since_last_shoot变量共享给我所有的敌人。因此,我尝试了这个方案:
spawn = function(entity)
entity.time_since_last_shoot = 0;
end
tick = function(entity, dt)
entity.time_since_last_shoot = entity.time_since_last_shoot + dt
if entity.time_since_last_shoot > 10 then
enemy = find_closest_enemy(entity)
shoot(entity, enemy)
entity.time_since_last_shoot = 0
end
end
然后,为每个实例创建一个独特的表格,并在调用生成和tick函数时将其作为第一个参数传递。然后在运行时将该表格映射回一个ID。这可能有效,但我有一些担心。
首先,这容易出错。脚本仍然可以意外创建全局状态,这可能导致稍后在同一个脚本甚至其他脚本中难以调试的问题。
其次,由于更新和tick函数本身是全局变量,因此在创建尝试使用相同接口的第二种类型的敌人时仍会遇到问题。我想我可以通过某种命名约定来解决这个问题,但肯定有更好的处理方法。
我发现了这个问题,它似乎正在询问同样的事情,但是接受的答案缺乏具体细节,并引用了Lua_setfenv函数,在Lua 5.3中不存在。它似乎被_ENV取代了,不幸的是我对Lua不太熟悉,无法完全理解和/或翻译这个概念。
[编辑] 基于@hugomg的建议的第三次尝试:
-- baddie.lua
baddie.spawn = function(self)
self.time_since_last_shoot = 0
end
baddie.tick = function(self, dt)
entity.time_since_last_shoot = entity.time_since_last_shoot + dt
if entity.time_since_last_shoot > 10 then
enemy = find_closest_enemy(entity)
shoot(entity, enemy)
entity.time_since_last_shoot = 0
end
end
然后在C++(使用sol2)中:
//在游戏启动时
sol::state lua;
sol::table global_entities = lua.create_named_table("global_entities");
//对于每种类型的实体
sol::table baddie_prototype = lua.create_named_table("baddie_prototype");
lua.script_file("baddie.lua")
std::function<void(table, float)> tick = baddie_prototype.get<sol::function>("tick");
//生成敌人类型的新实例时
sol::table baddie_instance = all_entities.create("baddie_instance");
baddie_instance["entity_handle"] = new_unique_handle();
//更新时
tick(baddie_instance, 0.1f);`
这符合我的预期,并且我喜欢这个接口,但我不确定对于更熟悉Lua的人是否会有最少惊喜的路径。主要是,我隐式的self参数使用以及原型/实例的区别。我对此理解得是否正确,或是否做了一些奇怪的事情?
对于你的第一个问题(意外创建全局变量),你可以依靠像luacheck这样的linter或者像Penlight的模块来防止你创建全局变量。
然后,为什么不把这些变量都变成本地变量呢? 我是说time_since_last_shoot和tick这两个变量。 这充分利用了Lua中最有用的功能之一——闭包。 如果你想要不同的tick函数,每个函数都有自己的变量,你可以这样做:
local function new_tick()
local time_since_last_shoot = 0
return function(entity_id, dt)
time_since_last_shoot = time_since_last_shoot + dt
if time_since_last_shoot > 10 then
local enemy = find_closest_enemy(entity_id)
shoot(entity_id, enemy)
time_since_last_shoot = 0
end
end
end
local tick_1 = new_tick()
local tick_2 = new_tick()
当然,你也可以使用环境变量解决这个问题,但是我认为局部变量和闭包是解决这个问题的更好的解决方案。
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?

在 Lua 5.3 中,_ENV 的工作方式是全局变量是从 _ENV 变量中读取字段的 "句法" 糖果。例如,一个执行以下代码的程序
local x = 10 y = 20 print(x + y)等价于
local x = 10 _ENV.y = 20 _ENV.print(x + _ENV.y)默认情况下,_ENV 是一个"全局表",其行为与您预期的全局变量的行为相同。但是,如果您创建了一个名为 _ENV 的局部变量(或函数参数),则在该变量的作用域中,任何未绑定的变量将指向该新环境,而不是指向通常的全局作用域。例如,下面的程序会打印 10:
local _ENV = { x = 10, print=print } -- 下面这行代码等价于 _ENV.print(_ENV.x) print(x)在您的程序中,使用这种技术的一种方法是为您的函数添加一个额外的参数以用作环境:
tick = function(_ENV, entity, dt) -- ... end然后,函数中的任何全局变量实际上只是访问 _ENV 参数中的字段,而不是实际的全局变量。
话虽如此,我不确定_ENV 是否是解决您问题的最佳工具。对于您遇到的第一个问题,“意外创建全局变量”,更简单的解决方案是使用一个代码检查器,在您赋值给未声明的全局变量时提醒您。至于第二个问题,您可以将更新和 tick 函数放在一个表中,而不是让它们成为全局变量。