2

我使用 LuaBind 在我的 C++ 应用程序中嵌入了 Lua。我需要有在多次运行中持续存在的变量,运行相同文件名的其他对象无法访问这些变量。

例如:假设我有一个名为NPC. AnNPC包含一个字符串,即它们运行的​​脚本的名称。创建an 时,NPC会创建一个名为 的变量Health。当一个NPC被击中时,他们会失去 5 点生命值。脚本在 Lua 中是这样的:

local health = 10

function onHit()
    health = health - 5
end

我遇到的问题是每个NPC运行这个脚本的人都没有自己的健康实例。例如,假设我创建NPC A,并从其生命值中减去 5。然后,我创建NPC B. 因为它会将生命值重置为 10,所以如果我告诉NPC A打印生命值,它会返回 10,即使它应该是 5。

如果我要为每个对象使用不同的 Lua 实例,那么它会以这种方式工作,但在这种情况下,我会一次得到数百个实例,我知道这不是一件好事。

有没有办法让变量在 Lua 中像这样工作?如果没有,是否有一种脚本语言可以有效地像这样工作?

作为参考,这是我正在测试的代码:

卢阿:

local health = 10;

function onHit()
    health = health - 5
    print_out(health)
end

C++:

class NPC
{
public:
   NPC(lua_State* inState);
   void onHit();

   const char* behavior;
   lua_State* luaState;  
};

NPC::NPC(lua_State* inState)
{
   luaState = inState;
   behavior = "testBehavior.lua";
   luaL_dofile(luaState, behavior);
}

void NPC::onHit()
{    
   luaL_loadfile(luaState, behavior); 
   luabind::call_function<int>(luaState, "onHit");
}

void print_out(int number) {
   cout << "Health : " << number << endl;
}

int main() 
{
   lua_State *myLuaState = luaL_newstate();
   luabind::open(myLuaState);

   luabind::module(myLuaState) [
      luabind::def("print_out", print_out)
   ];

   NPC test(myLuaState);
   NPC test2(myLuaState);
   test.onHit();
   test2.onHit();
   test.onHit();

   lua_close(myLuaState);
}
4

4 回答 4

8

您可以查看 Lua 的闭包。它看起来像这样:

function healthPoints()
    local health = 10
    return function()
        health = health - 5
        return health
    end
end

发生的情况是,每个 NPC 都有自己的功能和自己的计数器。每次它们被击中时,您只需调用该onHit函数。你会像这样使用它:

npc1.onHit = healthPoints()
npc2.onHit = healthPoints()
npc1.onHit() -- Now at 5, while npc2 is still at 10

您可以像这样添加参数:

function healthPoints(hp)
    local health = hp
    return function(dmg)
        health = health - dmg
        return health
    end
end

npc1.onHit = healthPoints(100)
npc1.onHit(-12)

我认为值得一试。

于 2012-12-30T05:05:26.677 回答
4

作为参考,这是我正在测试的代码:

这里有很多错误。你不断地重新运行你的 Lua 脚本;这就是它不断重置的原因。另一个问题是您的脚本正在创建一个全局函数,因此每次运行它时,您都会得到一个的全局函数,该函数使用一个 local变量。

停止使用全局变量。每个 NPC 都是一个单独的对象。因此,它需要具有特定于对象的数据。

behavior = "testBehavior.lua";
luaL_dofile(luaState, behavior);

这不会创建任何特定于对象的数据。它只是运行一个 Lua 脚本并完全丢弃任何返回值。除非该脚本实际上全局存储了特定于对象的东西,否则不会创建任何特定于对象的数据。

您的 Lua 脚本需要做的是返回一个表,其中包含脚本对对象所需的特定于对象的数据。脚本应如下所示:

local function onHit(self)
    self.health = self.health - 5
end

return {
  health = 10,
  onHit = onHit,
}

您的 C++ 代码需要将此表存储在 NPC 类中,然后使用它。这可以通过 Luabind 调用轻松完成。您的构造函数应如下所示:

NPC::NPC(lua_State* L)
{
    behavior = "testBehavior.lua";
    int err = luaL_loadfile(L, behavior);
    //Handle compiler errors. DON'T FORGET THIS!
    luabind::object func = luabind::from_stack(L, -1);
    lua_pop(L, 1);
    luaData = func(); //Catch exceptions for runtime errors
    lua_pop(L, 1);
}

你不是lua_State* luaState在你的班级里,而是luabind::object luaData在你的班级里。如果您需要lua_State,您可以随时从luabind::object.

要调用 LuaonHit函数,只需使用luabind::object接口:

void NPC::onHit()
{    
    luaData["onHit"](luaData);
}

请注意,您不会重新运行 Lua 脚本。这就是你的问题所在。您只是在调用 Lua 脚本已经定义的函数。

现在,您似乎想使用本地而不是表内存。没关系; 它会阻止 C++ 代码直接访问health(并非没有诡计)。它会简化我们的代码,因为我们不必传递luaDataonHit. 你可以在 Lua 中这样做:

local health = 10
local NPC = {}

function NPC.onHit()
  health = health - 5
end

return NPC

NPC构造函数不需要改变;只是我们的呼吁onHit

void NPC::onHit()
{    
    luaData["onHit"]();
}

如果您对使用全局变量一无所知,您可以在环境中玩游戏,但这相当复杂。它将在各个脚本调用之间提供有保证的隔离。

于 2012-12-30T21:30:41.930 回答
2

我不了解Lua,所以我不会谈论这个。该问题也标记为python。为此,我认为您可以序列化所有“重要”的局部变量。

或者,您可以查看我的技巧,在此处保留一个名为 PyDON 的字典:http: //code.activestate.com/recipes/577508-pydon-an-alternative-to-xml/

还要检查一下,我认为这会更有帮助:如何将 Python 字典序列化为字符串,然后再返回字典?

于 2012-12-30T03:57:43.850 回答
2

这似乎有点矫枉过正,但为什么不将所需的值存储在 SQLite 表中呢?

于 2012-12-30T04:00:34.320 回答