Writing an iPhone Game Engine (Part 3- Scripting)
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.)
- 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:
- function drawText(str, screenPosX, screenPosY);
then in Lua side, when the following Lua script is executed:
- 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:
- int drawText(lua_State* luaState)
- float screenPosY = (float) lua_tonumber(luaState, -1); // get the value 160
- float screenPosX = (float) lua_tonumber(luaState, -2); // get the value 240
- const char* str = lua_tostring(luaState, -3); // get the value "Ready Go~"
- printf("Text '%s' draw at (%f, %f)\n", str, screenPosX, screenPosY);
- int textWidth= strlen(str);
- lua_pushnumber(luaState, textWidth); // return a value to Lua
- return 1; // number of values return to Lua
- 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:
- function initEngineConfig(date)
- print('initialize engine on ' .. date);
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:
- lua_getglobal(luaState, "initEngineConfig"); // get the function to the stack
- lua_pushstring(luaState, "18th Aug, 2011"); // push the argument of the function
- lua_call(luaState, 1, 0); // execute the function
- 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:
We can do this in C too. In Lua, the colon syntax is just a short form for writing the statement:
- gameObjectA.update(gameObjectA, timeSlice);
- lua_getglobal(luaState, "gameObjectA"); // for getting the 'update' function of 'gameObjectA'
- lua_getfield(luaState, -1, "update"); // get the 'update' function of 'gameObjectA'
- lua_insert(luaState, -2); // swap the order of "gameObjectA" and "update"
- // so that "gameObjectA" becomes an argument
- lua_pushnumber(luaState, 1.0f/30.0f); // push the timeSlice argument on the stack.
- lua_call(luaState, 2, 0); // execute the functions.
This is basically how Lua interacts with C, but you also need to know how to represent C structure as user data or light user data. You may also need to know 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.
 Lua manual: http://www.lua.org/manual/5.1/manual.html
 Lua user data: http://www.lua.org/pil/28.1.html
 Lua light user data: http://www.lua.org/pil/28.5.html
 LUA_REGISTRY_INDEX: http://www.lua.org/pil/27.3.2.html