在C中嵌套Lua元表



在一个3D场景中,我有一个物体,我想用Lua移动它的位置。

box.position.x = 10

box有一个元表("Object"),也有position ("Vec")。对象将__newindex__index设置为分别调用C函数NewIndexObjectIndexObject。与Vec (NewIndexVecIndexVec)相同。

对象有一个id,所以它可以在存储在场景中的列表中识别,当box.position被访问时一切都很好,C函数IndexObject被调用,我可以从堆栈中提取id,只是当box.position.x = 10被执行时,'NewIndexVec'被调用,堆栈上唯一的东西是{table, x, 10}所以没有办法识别对象来改变它的x位置。

是否存在将值推入本地状态的方法?的帮助!

更新:感谢您快速回复我,下面我已经尽可能多地提炼了代码。如果你运行这段代码,它似乎可以工作但我有注释,我卡住了,它只是获取数组中的第一个对象但我需要根据它的ID选择它,谢谢提前

struct Obj
{
    std::string id;
    int x,y,z;
    Obj()
    {
        x = 10; y = 20; z = 30;
        id = "12345";
    }
};
//array of external objects
std::vector<Obj> objects;
int NewObject(lua_State * L)
{
    Obj obj;
    objects.push_back(obj);
    lua_newtable(L); 
    luaL_getmetatable(L, "MT_Object");
    lua_setmetatable(L, -2);
    lua_pushstring(L, "id");
    lua_pushstring(L, obj.id.c_str());
    lua_settable(L, 1);
    lua_newtable(L); 
    luaL_getmetatable(L, "MT_Vec");
    lua_setmetatable(L, -2);
    lua_pushinteger(L, obj.x);
    lua_setfield(L, -2, "x"); 
    lua_pushinteger(L, obj.y);
    lua_setfield(L, -2, "y"); 
    lua_pushinteger(L, obj.z);
    lua_setfield(L, -2, "z"); 
    lua_setfield(L, -2, "position");

    return 1;
}
int IndexVec(lua_State * L)
{
    // How do I get the correct object so I can pass its value back
    Obj &dunnoObj =  objects[0];
    std::string key = luaL_checkstring(L,-1);
    if(key == "x")
        lua_pushinteger(L,dunnoObj.x);
    else if(key == "y")
        lua_pushinteger(L,dunnoObj.y);
    else if(key == "z")
        lua_pushinteger(L,dunnoObj.z);
    return 1;
}

int NewIndexVec(lua_State * L)
{
    // How do I know which object's value to update
    Obj &dunnoObj =  objects[0];
    std::string key = luaL_checkstring(L,-2);
    int value = luaL_checkinteger(L,-1);
    if(key == "x")
        dunnoObj.x = value;
    else if(key == "y")
        dunnoObj.y = value;
    else if(key == "z")
        dunnoObj.z = value;
    return 0;
}
int main()
{
    lua_State * L = luaL_newstate();
    luaL_openlibs(L);

    luaL_Reg objreg[] =
    {
        { "new", NewObject },   
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Object");
    luaL_register(L, 0, objreg);
    lua_setglobal(L, "Object");

    luaL_Reg reg[] =
    {
        { "__index", IndexVec },    
        { "__newindex", NewIndexVec },
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Vec");
    luaL_register(L, 0, reg);
    lua_setglobal(L, "Vec");

    int res = luaL_dostring(L, "box = Object.new()   box.position.x = 1000   print(box.id .. " , " ..box.position.x .. " , " ..  box.position.y .. " , " .. box.position.z)");
    if(res)
        printf("Error: %sn", lua_tostring(L, -1));

    lua_close(L);
    return 0;
}

如果我没理解错的话,你什么都不用做。表是通过引用来跟踪的,所以如果NewIndexVec的第一个参数是box.position,那么它不需要知道任何关于box的信息。

如果这个答案由于某种原因不能工作,那么我需要更多关于你的数据结构的信息来理解你的问题。

基本上,box.position需要返回一些obj, obj.x = 10是一个有效的操作,并确切地改变你想要改变的。


问题是您试图将相同的数据保存在两个不同的位置。将所有数据保存在c++结构体中,然后让NewObject返回一个假装为表的userdata。Object和position字段都应该是相同的Obj*,但是它们可以有不同的元表来模拟不同的字段集。

谢谢,我已经发布了工作的代码

struct Obj
{
    unsigned int id;
    int x,y,z;
    Obj()
    {
        x = 10; y = 20; z = 30;
        id = rand();
    }
};
//array of external objects
std::map<unsigned int,Obj> objects;
int NewObject(lua_State * L)
{
    Obj obj;
    objects[obj.id] = obj;
    lua_pushinteger(L, obj.id);
    luaL_getmetatable(L, "MT_Object");
    lua_setmetatable(L, -2);
    return 1;
}

int IndexObj(lua_State * L)
{
    unsigned int objid = luaL_checkinteger(L,1);
    std::string key = luaL_checkstring(L,-1);
    if(key == "position" )
    {
        Obj *a = (Obj *)lua_newuserdata(L, sizeof(Obj));
        *a = objects[objid];
        luaL_getmetatable(L, "MT_Vec");
        lua_setmetatable(L, -2);
    }
    else if(key == "id")
        lua_pushinteger(L, objid);  
    else
        lua_pushnil(L);
    StackDump(L);
    return 1;
}
int IndexVec(lua_State * L)
{
    Obj *a = (Obj *)lua_touserdata(L, 1);
    std::string key = luaL_checkstring(L,-1);
    if(key == "x")
        lua_pushinteger(L,a->x);
    else if(key == "y")
        lua_pushinteger(L,a->y);
    else if(key == "z")
        lua_pushinteger(L,a->z);
    return 1;
}

int NewIndexVec(lua_State * L)
{
    Obj *a = (Obj *)lua_touserdata(L, 1);
    Obj &objRef =  objects[a->id];
    std::string key = luaL_checkstring(L,-2);
    int value = luaL_checkinteger(L,-1);
    if(key == "x")
        objRef.x = value;
    else if(key == "y")
        objRef.y = value;
    else if(key == "z")
        objRef.z = value;
    return 0;
}
int main()
{
    lua_State * L = luaL_newstate();
    luaL_openlibs(L);

    luaL_Reg objreg[] =
    {
        { "new", NewObject },   
        { "__index", IndexObj },
        { "__newindex", NewIndexObj },
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Object");
    luaL_register(L, 0, objreg);
    lua_setglobal(L, "Object");

    luaL_Reg reg[] =
    {
        { "__index", IndexVec },    
        { "__newindex", NewIndexVec },
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Vec");
    luaL_register(L, 0, reg);
    lua_setglobal(L, "Vec");
    int res = luaL_dostring(L,  "box = Object.new()   box.position.x = 1000 "
                                "print(box.id .. " , " ..box.position.x .. " , " ..  box.position.y .. " , " .. box.position.z)");
    if(res)
        printf("Error: %sn", lua_tostring(L, -1));
    lua_close(L);
    return 0;
}

最新更新