2011-06-09 9 views
6

Estoy trabajando en un motor de juego en C++ usando Lua para definir NPC.¿Cómo mapear directamente una variable de Lua a una variable de C++?

puedo definir un NPC prototípico de esta manera:

orc = 
{ 
    name = "Generic Orc", 
    health = 100 
} 

function orc:onIdle() 
    print("Orc idles...") 
end 

y luego generar una instancia de "Orc" con entitySpawn(orc). Esta es una función de C++ que lee valores como salud y nombre de la tabla dada, crea un objeto Entity en C++ con los valores dados y además crea una tabla Lua para el NPC específico.

Ahora, me gustaría tener una conexión directa entre la variable orc.health en Lua y la variable mHealth miembro del objeto Entity correspondiente en C++, por lo que podría asignar un valor en Lua y al instante utilizarlo en C++ y viceversa .

¿Esto es posible? ¿O tengo que hacer uso de las funciones setter/getter? He echado un vistazo a userdata light y llegué al punto de almacenar un puntero a la variable C++ en Lua, pero no pude asignar un valor.

Respuesta

6

Esto es posible. Supongo que entitySpawn devuelve la tabla que C++ creó para la entidad. También supondré que puede exponer una función de C++ que toma la tabla de una entidad y devuelve el estado actual, y de manera similar para la configuración. (Se puede utilizar un puntero userdata ligero a objeto de C++ como miembro de esta tabla para implementar esto.)

Así que la manera fea se vería así:

local myOrc = entitySpawn(orc) 
local curHealth = getEntityHealth(myOrc) 
setEntityHealth(myOrc, curHealth + 10) 

Para que esto sea más bonito, podemos Diviértete con metatablas. Primero, colocaremos los accesorios para todas las propiedades que nos interesan.

entityGetters = { 
    health = getEntityHealth, 
    -- ... 
} 
entitySetters = { 
    health = setEntityHealth, 
    -- ... 
} 

A continuación, vamos a crear un metatable que utiliza estas funciones para manejar las asignaciones de propiedad.

entityMetatable = {} 

function entityMetatable:__index(key) 
    local getter = entityGetters[key] 
    if getter then 
     return getter(self) 
    else 
     return nil 
    end 
end 

function entityMetable:__newindex(key, value) 
    local setter = entitySetters[key] 
    if setter then 
     return setter(self, value) 
    end 
end 

Ahora tiene que hacer entitySpawn asignar el metatabla. Haría esto con la función lua_setmetatable.

int entitySpawn(lua_State* L) 
{ 
    // ... 
    // assuming that the returned table is on top of the stack 
    lua_getglobal(L, "entityMetatable"); 
    lua_setmetatable(L, -2); 
    return 1; 
} 

Ahora se puede escribir de la manera agradable:

local myOrc = entitySpawn(orc) 
myOrc.health = myOrc.health + 10 

Tenga en cuenta que esto requiere que la tabla que devuelve entitySpawn hace no tienen un conjunto de propiedades de salud. Si lo hace, entonces el metatable nunca será consultado para esa propiedad.

Puede crear los entityGetters, entitySetters y entityMetatable mesas, así los __index y __newindex metamétodos, en C++ en lugar de Lua si se siente más limpio para usted, pero la idea general es la misma.

+0

¡Gracias, eso es lo que necesito! Parece que mi investigación fue en la dirección completamente equivocada :) – Daerst

2

No tengo tiempo para darle ningún código, pero tenga en cuenta que orc.health es lo mismo que orc["health"]; es decir, orc es una tabla y "health" es un elemento. Con esto en mente, puede cambiar index y newindexmetamethods de su tabla para tener su propio comportamiento específico. Almacene su instancia orc "real" como algunos metadatos privados, luego úselo para actualizar.

Cuestiones relacionadas