我有一个名为Entity的类,它有许多函数,如onPickup,onDrop,onUse等。我想要做的是,编写一个定义所有这些函数的脚本,并使它们可以从C ++函数中调用。因此,C ++中定义的函数只是调用它们具有某些功能的相应Lua函数。
但问题是,我希望我编写的每个脚本,程序中的每个实体都在其自己的范围内工作。
我正在使用LuaBind,而且我之前没有Lua的经验,所以我在这里有点迷失。
答案 0 :(得分:3)
我不使用lua bind但这可能有所帮助。我们的想法是在你的C ++类中注册lua函数,并在你的C ++类中保留对lua函数的引用。
要定义可从C / C ++调用的lua函数,我使用luaL_ref来存储对我的C ++对象中的回调函数的引用。
// a little helper function
template <typename T>
T *Lua_getUserData(lua_State *L) {
assert(lua_isuserdata(L, 1) == 1);
T **v = (T **) lua_touserdata(L, 1);
assert(v != NULL);
return *v;
}
int lua_FormRegisterMethods(lua_State *L) {
Entity *f = Lua_getUserData<Entity>(L);
assert(lua_istable(L, 2) == 1); // check the next parameter is a table
lua_pushvalue(L,2); // dup the table
f->LuaTable = luaL_ref(L, LUA_REGISTRYINDEX); // keep a reference to the table
lua_getmetatable(L, 2); // get the metatable
lua_pushstring(L, "OnClick");
lua_rawget(L, -2); // get the OnClick Lua Function
f->LuaMethod = luaL_ref(L, LUA_REGISTRYINDEX); // save a reference to it
return 0;
}
然后你可以在你的C ++事件中获得lua方法
lua_rawgeti( LuaInstance->L, LUA_REGISTRYINDEX, LuaMethod );
assert(lua_isfunction(LuaInstance->L, -1) == 1);
现在你可以将自己设置为之前保存的表格来调用此函数。 HTH
答案 1 :(得分:1)
您可以使用例如
调用Lua函数int callLuaFunction(lua_State* lua_state) {
return luabind::call_function<int>(lua_state, "myluafunction", param1);
}
如果Lua函数返回一个int并取一个参数。
我很确定你可以根据需要制作尽可能多的lua_State
。只需将实体的正确版本传递给call_function
。
答案 2 :(得分:0)
要完全实现这一点,你可能会想要在Lua的一些更深奥的位中挖掘一下。虽然值得花时间。我将展示一个非常精简的版本,我处理了这个问题。请注意,这里有很多小部分都在一起工作 - 主要是保存和调用已保存的函数以及使用c ++对象作为Lua用户数据。
首先,我们需要一个c ++类,它将事件处理程序(lua函数)存储为lua引用,这是简单的int。我在这里使用一个数组,但你可以使用任何有意义的东西。这里发生的主要事情是你希望能够调用int引用引用的lua函数。
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include <string>
#include <iostream>
#include <assert.h>
using namespace std;
enum enum_event_types {
ON_USE, ON_DROP, ON_WHATEVER, EVENT_COUNT
};
class Entity {
private:
int events[EVENT_COUNT];
public:
lua_State* lua;
void setEventHandler(int event, int ref) {
assert(event < EVENT_COUNT);
events[event] = ref;
}
void callEventHandler(int event) {
int error;
assert(event < EVENT_COUNT);
// to call the function we need to get it from the registry index
lua_rawgeti(lua, LUA_REGISTRYINDEX, events[event]);
error = lua_pcall(lua, 0, 0, 0); // use protected call for errors
if (error) {
printf("error: %s", lua_tostring(lua, -1));
lua_pop(lua, 1);
}
}
};
现在您想要将您的Entity类公开给Lua。如果你不熟悉如何做到这一点,那就有一篇好文章here。基本上,我们正在将从Entity.new()返回的用户数据设置为指向指针的指针。这是Lua不垃圾收集你的对象。然后为“实体”创建一个元表 保持暴露给Lua的所有方法。
int L_newEntity(lua_State* L) {
Entity **e = (Entity **)lua_newuserdata(L, sizeof(Entity *));
*e = new Entity();
(*e)->lua = L;
lua_getglobal(L, "Entity");
lua_setmetatable(L, -2);
return 1;
}
int L_setOnUse(lua_State* L) {
Entity** e = (Entity**) lua_touserdata(L, 1);
lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
(*e)->setEventHandler(ON_USE, ref);
return 0;
}
// this will be exposed to Lua as a table called Entity
static const luaL_Reg L_entityMethods[] = {
{"new", L_newEntity},{"setOnUse", L_setOnUse},{NULL, NULL}
};
现在设置Lua状态并创建Entity表并创建测试。测试将创建一个Entity并将其事件处理程序设置为传递给它的Lua函数。最后测试是否由c ++对象调用Lua函数。
int main() {
Entity** e;
int error;
lua_State* L=lua_open();
luaL_openlibs(L);
luaL_register(L, "Entity", L_entityMethods);
lua_pushvalue(L,-1);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
luaL_loadstring(L,
"e = Entity.new(); "
"e:setOnUse(function()"
" print('Some of them want to use you')"
"end);");
error = lua_pcall(L, 0, 0, 0);
if (error) {
printf("error: %s", lua_tostring(L, -1));
lua_pop(L, 1); /* errors must be popped from stack */
}
lua_getglobal(L, "e");
if (lua_isuserdata(L, 1)) {
e = (Entity**) lua_touserdata(L, 1);
(*e)->callEventHandler(ON_USE);
}
return 0;
}
这远未完成。首先,如果您需要设置两次事件处理程序,则需要先使用luaL_unref清除旧引用。其次,您可能希望传递有关事件处理程序发生的事件的一些数据。当前事件处理程序不会获取任何数据,因此api的用户很少继续。它应该至少传递对调用事件的对象的引用。这可用于在Lua中创建非常强大且可用的Apis。祝你好运!