Hace un tiempo comentamos cómo estábamos
integrando Lua en el proyecto. En aquella ocasión nos centramos en utilizar Swig para acceder desde Lua a funciones en C++, ahora vamos a explicar cómo acceder desde C++ a funciones y datos en Lua.
Para esta parte, y como suele ser habitual, dependiendo de las necesidades del proyecto será mejor utilizar una alternativa u otra. En nuestro caso, queremos utilizar Lua para realizar la lógica de la interfaz de usuario e implementar las máquinas de estados para la inteligencia artificial en el simulador. Como ya descartamos en su momento utilizar Luabind y tolua++, para esta parte utilizaremos la API de Lua para enviar y recibir información entre C++ y Lua.
La interfaz de usuario la estamos desarrollando utilizando la librería CEGUI, que nos ofrece la interfaz
ScriptModule para poder utilizar algún sistema de scripting. En nuestro proyecto, utilizamos la clase CLuaManager que creamos para realizar los bindings mediante Swig, y ahora hacemos que herede de ScriptModule, implementando todos los métodos que sean necesarios. Una vez realizado esto, sólo tendremos que indicarle a CEGUI en su inicialización que utilice nuestro modulo de scripting, lo que quedaría algo así:
CEGUI::ScriptModule* script_module = CLuaManager::getInstance();
m_system = new CEGUI::System(m_renderer, 0, 0, script_module) ;
Con esto conseguimos que CEGUI se encargue de llamar a la funciones Lua que le indiquemos para cada elemento de la interfaz de usuario.
El otro uso que le vamos a dar a Lua va a ser la implementación de los estados de los agentes dentro del simulador. Veamos esto con un poco más de detalle:
Un estado en Lua va a ser una tabla de funciones con cuatro entradas, las cuales son "Enter", "Execute", "Exit", y "OnMessage", que son respectivamente las funciones a ejecutar al entrar en el estado, al ejecutar el estado, al salir del estado, y al recibir un mensaje en dicho estado. Un estado escrito en Lua quedaría así:
State_PlayOn = {}
State_PlayOn["Enter"] = function(agente)
     print("Entrando en el estado Play On")
end
State_PlayOn["Execute"] = function(agente)
     print("El estado Play On está ejecutandose")
end
State_PlayOn["Exit"] = function(agente)
     print("Saliendo de Play On")
end
State_PlayOn["OnMessage"] = function(agente, msg)
     print("Se ha recibido un mensaje en el estado Play On")
     return true;
end
Ahora veamos por ejemplo como llamar desde C++ a la función "Enter" del estado "State_PlayOn" descrita en Lua: (simplificando un poco)
lua_settop(luaVM, 0);
lua_getglobal(luaVM, "State_PlayOn");
if(!lua_istable(luaVM, -1)) {
     printf("Scripted state \"%s\" doesn't exist", "State_PlayOn");
} else {
     lua_pushstring(luaVM, "Enter");
     lua_gettable(luaVM, -2);
     if(!lua_isfunction(luaVM, -1)) {
         printf("Function \"%s\" in state \"%s\" doesn't exist", "Enter", "State_PlayOn");
     } else {
         lua_pushlightuserdata(luaVM, agente);
         lua_pcall(luaVM, 1, 0, 0);
     }
}
Que básicamente quiere decir: obtenemos la tabla "State_PlayOn", accedemos al elemento "Enter", introducimos el agente como parámetro de entrada a la función, y ejecutamos la función. Faltaría comprobar el valor que devuelve "pcall" para ver si todo ha ido bien y limpiar la pila de Lua. Podéis ver todo esto y como implementar la máquina de estados mirando el fichero CStateMachine.h en el
SVN de Project Football.