Integrating Lua and my C++ Game Engine

If you want to cut to the chase you can download the code here.

This isn’t a very well written blog post, it’s more of a dump of my thoughts as I tested out LuaBridge and didn’t get to trying out OOLua.

Once again I am starting to integrate Lua in to the next generation of my engine. It’s never a completely smooth process. Exactly where to draw the line between C++ and Lua logic is never immedietly clear.

This is the fifth major revision of my engine (that I can remember) and it’s move toward a component based system has made the dividing line more obvious.

Previously I had driven the engine completely from Lua, the main loop essentially being the first script that the engine loaded. This script drove everything. With a component based system and a new custom editor the “data driving” is done by the editor as it packs levels or scenes together.

Bridging the divide

In previous incarnations of my engine integration of Lua and C++ has be done with:

I never want to handcraft it ever again, it’s just too time consuming and brittle. SWIG, while easy to use and clever, produces glue code that is slower than needs be and very complicated. tolua++ produces better code than SWIG and has some really nice features but seems to be unmaintained and there are some bugs that need to be worked around.

LuaBridge over OOlua

So I turned to the internet and the top two contenders (that don’t require boost) seem to be OOLua and LuaBridge. They both employ a set of C++ templates to wrap your code, exposing it to Lua.

OOLua has some nice tables showing it is the fastest thing in the world which enticed me BUT it requires Premake, Cpp Unit and Google Mock to build it. This really put me off. I just want to wrap my code, not install another build system and unit testing framework. LuaBridge doesn’t require any external tools or libraries.

LuaBrdge has a more pleasant syntax and the underlying template code seems easier to decipher. To expose a simple struct (taken from the OOLua docs)

1
2
3
4
struct foo
{
  int bar(float);
};

In OOLua you would enter:

1
2
3
4
5
6
7
OOLUA_CLASS_NO_BASES(foo)
    OOLUA_NO_TYPEDEFS
    OOLUA_ONLY_DEFAULT_CONSTRUCTOR
    OOLUA_MEM_FUNC_1(int/*return type*/
                   ,bar/*function name*/
                   ,float)/*parameter type*/
OOLUA_CLASS_END

while in LuaBridge:

1
2
3
4
5
getGlobalNamespace (L)
    .beginNamespace ("vox")
    .beginClass<foo>("foo")
        .addFunction("bar", &foo::bar)
    .endClass();

LuaBridge seems more concise, less brittle (it uses C++ templates to infer the paramter types and return types) and quite frankly who wants CAPITALS ALL THROUGH THEIR CODE?

Both seem like very well written libraries though so it’s really a matter of personal preference as opposed to some major technical flaw in OOlua.

So I decided to test LuaBridge first. If I could get what I wanted from it then I’d move forward.

Overview of Requirements.

The scripts that I want to attach to my GameObjects are more like behaviours than massive all emcompassing AI scripts. This design is something I learned to like while playing with Unity3D a few months ago. While it adds a certain amount of managment overhead on the engine side it allows a lot of the day to day scripting of a game to be built from small scripts. For example you can write a small script to look at the player and attach it to every enemies head, add a parameter to limit it to when the player is within n meters and you’ve got a nice reusable component that anyone (even non programmers) can drop on trees, pickups, bosses, chickens, anything you want.

I want my scripts to be able to be updated

  • every frame
  • before physics
  • after physics
  • on a trigger
  • on a collisions

Or any combination of these. So I need a mechanism where the script lets the engine know how to use the script.

Prototype Interface

Rather then screw up my codebase I created a test project to play with LuaBridge. You can get the code here

All of the code is in the one cpp file main.cpp. test.lua is a test script to attach to GameObjects. It depends on class.lua (fairly generic class definition based on the lua wiki example) and utility.lua (old code, probably also taken from the lua wiki over the years to manipulate tables).

The code is fairly well commented but here is an overview of how it works.

GameObject is a hollowed out container class for entities in a game. It has a few token members to test exposing them to lua.

LoadScript loads a lua script and executes it. The script is expected to register a global function register_script that is called returning a class definition (lua table). This definition is instanstiated and attached to the game object. Load script does some simple caching to prevent test.lua being run multiple times.

Once numscripts GameObjects are created with test.lua attached the GameObjects are ticked. The GameObject::Tick function looks for a function called tick in it’s lua class and if it’s there calls it.

Once that’s all done, everything is deleted to see if we’re leaking memory.

Thoughts

  • really like LuaRef it’s a very nice way to program lua.
  • don’t like the way the scripts have to define a callback function, would prefere to return the class definition during the inital script run. However I don’t want to play with the Lua stack and LuaBridge doesn’t seem to provide a clean way to get the table off of the stack.
  • In tolua++ you could write and expose a C++ function is such a way as it would appear, in Lua, to return multiple values. This was very nice and Lua-ish (search for Multiple returned values here). I can’t see how to do this in LuaBridge.

Gotchas.

Four Character Literal expansion bit me:

1
2
3
4
5
6
7
8
void Tick(float dt){
    LuaRef tickfunc = mLuaClass['tick'];
    if(tickfunc.isFunction()){
        tickfunc(mLuaClass, dt);
    }else{
        std::cout << mName << " tick isn't a function: " << tickfunc.type() << std::endl;
   }
}

tickfun will ALWAYS be nil in because clang will convert ‘tick’ in to an integer (note the single quotes, not double quotes for the string tick). You need to access the table thusly:

1
LuaRef tickfunc = mLuaClass["tick"];

to get access to the function.

There is a setting in XCode to warn about this conversion but it’s off by default. Using single quotes is a habit I’ve picked up from Python unfortunately.

Can’t use LuaRef in a std:: container

Because the default constructor for a LuaRef takes one parameter (the lua_State that it belongs to) if you try to create a map like:

1
std::map<std::string, LuaRef>

You will get a compiler error.

Comments