Adding script support to an engine has many benefits such as writing game play code without recompile the engine source code which may takes a long time. It also can draw a much clear boundary between the game code and the engine code. I chose to use Lua(ver. 5.1.4) as it is easy to embed and its size is small. As I am no expert on Lua, I would like to write about what I have learnt on how to bind Lua and C/C++.

Calling C function from Lua
First, you need to create a lua_State*, where all Lua operations are done within it, by lua_open() (or you may use lua_newState() if you need to hook up your memory allocator to Lua.)

  1. lua_State* luaState= lua_open();

Lua and C can exchange data through a virtual stack. Both Lua and C can push and pop data from or to the stack. Say, we have already register a C function for Lua to use with function signature:

  1. function drawText(str, screenPosX, screenPosY);

then in Lua side, when the following Lua script is executed:

  1. textWidth= drawText(“Ready Go~”,  240, 160);

3 values will be pushed to the Lua Stack:

We can get the values from the stack in C using an absolute index counting from the bottom of the stack (start from 1) or a relative index to the top of the stack (start from -1).

Then we can retrieve the values in stack inside the C function called by Lua with the following code:

  1. int drawText(lua_State* luaState)
  2. {
  3.     float screenPosY = (float) lua_tonumber(luaState, -1);  // get the value 160
  4.     float screenPosX = (float) lua_tonumber(luaState, -2);   // get the value 240
  5.     const char* str = lua_tostring(luaState, -3);                     // get the value “Ready Go~”
  6.     printf(“Text ‘%s’ draw at (%f, %f)\n”, str, screenPosX, screenPosY);
  7.     int textWidth= strlen(str);
  8.     lua_pushnumber(luaState, textWidth);                            // return a value to Lua
  9.     return 1;                                                                           // number of values return to Lua
  10. }
when the C function exit, the stack will look like:
After Lua get the return value from the C function, the parameter and the return value of the C function will be popped out of the stack.
But before Lua can execute the drawText(), remember to register it to the lua_State* by:
  1. lua_register(luaState, ”drawText“, drawText);



Calling Lua functions from C
We can also call Lua functions from C. For example, we have declare a function in Lua script for initializing the engine configurations:

  1. function initEngineConfig(date)
  2. print(‘initialize engine on ‘ .. date);
  3. end

As Lua is a typeless language, it also treats functions as variables. So we need to push the Lua variable ‘initEngineConfig’ into the stack with its arguments with the following C code:

  1. lua_getglobal(luaState, “initEngineConfig“);   // get the function to the stack
  2. lua_pushstring(luaState, “18th Aug, 2011″);   // push the argument of the function
  3. lua_call(luaState, 1, 0);                          // execute the function

(You may also use lua_call() to get more debug info  when error occurs)
The above C codes is equivalent to calling a function in Lua:

  1. initEngineConfig(“18th Aug, 2011″);

We can also use similar technique to execute an object’s method in C. For instance, we can call an object’s method in Lua:

  1. gameObjectA:update(timeSlice);

We can do this in C too. In Lua, the colon syntax is just a short form for writing the statement:

  1. gameObjectA.update(gameObjectA, timeSlice);
So, we need to push the ‘update’ function of ‘gameObjectA’ onto the Lua stack with 2 arguments:
  1. lua_getglobal(luaState, “gameObjectA“); // for getting the ‘update’ function of ‘gameObjectA’
  2. lua_getfield(luaState, -1, “update“);     // get the ‘update’ function of ‘gameObjectA’
  3. lua_insert(luaState, -2);                           // swap the order of “gameObjectA” and “update”
  4.                                                                 // so that gameObjectA” becomes an argument
  5. lua_pushnumber(luaState, 1.0f/30.0f); // push the timeSlice argument on the stack.
  6. lua_call(luaState, 2, 0);                      // execute the functions.

LUA_REGISTRY_INDEX for creating a variable in C without worrying conflicts in variable name. After knowing these things you may want to try some binding library to generate the binding. But I hope these little binding methods can help someone who want to bind Lua and C on their own.


Reference:

[3] Lua light user data: