I should be writing my 3rd Lua and why you should be using it in your projects.

What is Lua

Chances are everyone reading this already knows what Lua is, but here it is anyway:

Lua is a powerful, fast, lightweight, embeddable scripting language. Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode for a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping.

That’s good and all, but why Lua is heavily used in the videogame industry?

A Scripting Language for Games

Although not specifically designed to be embedded in games, Lua has characteristics that make it very appealing to the videogame industry:

  • It’s lightweight
  • It’s fast
  • It doesn’t need loads of memory to run
  • It’s highly portable (all source code is written in ANSI-C)
  • It’s very easy to embed
  • It has a very well designed API
  • It’s syntax is simple and easy to learn
  • It’s extensible
  • It has a active mailing list
  • Has a permissive license

Lets take a look at each one of these items.

It’s lightweight

Compiling Lua 5.1.4 with MinGW in my system results in a liblua.a library with only 206,832 bytes. The library has the Lua virtual machine to run code, the compiler, and many auxiliary libraries. It’s possible to strip the parser, resulting in an even smaller library. Also, auxiliary libraries can be left out if not needed.

When the parser is stripped out of the library, a companion application, luac, can be used to precompile Lua source code into bytecode which then can be loaded and run.

It’s fast

Take a look at Computer Language Benchmarks Game comparison chart and judge by yourself. This must of course be taken with a grain of salt since the programs used to compute the running time are not representative of what you usually find in game source code written in scripting languages.

I should also note that Lua’s compiler is also pretty fast in converting Lua source code into bytecode, though I don’t have any data to back me up on this.

If stock Lua isn’t fast enough for you, you can try using Kore VM which requires a paid license.

It doesn’t need loads of memory to run

Check minimum requirements for eLua. eLua is a full implementation of Lua for the embedded world.

It’s highly portable (all source code is written in ANSI-C)

Lua compiles out of the box in a number of processors and OSes. x86, x64, ARM, PowerPC, Windows, Linux, Mac, iOS… Also check pbLua, a Lua port that runs on LEGO Mindstorms.

It’s very easy to embed

To embed Lua in your application all you have to do is:

#include <stdio.h>
 
   
 
  #include "lua.h"
 
  #include "lauxlib.h"
 
  #include "lualib.h"
 
   
 
  int main( void )
 
  {
 
    /* create state */
 
    lua_State *L = lua_open();
 
    if ( L == NULL )
 
    {
 
      fprintf( stderr, "Not enough memory" );
 
      return -1;
 
    }
 
   
 
    /* load and run a Lua script */
 
    if ( luaL_dofile( L, "script.lua" ) != 0 )
 
    {
 
      /* the error message is pushed onto the Lua stack */
 
  bail_out:
 
      fprintf( stderr, "%s", lua_tostring( L, -1 ) );
 
      lua_close( L );
 
      return -1;
 
    }
 
   
 
    /* now that the script has run we can call any global function defined in it */
 
   
 
    /* push the function onto the Lua stack */
 
    lua_getglobal( L, "GlobalFunc" );
 
   
 
    /* push the first argument */
 
    lua_pushliteral( L, "first argument" );
 
   
 
    /* push the second argument */
 
    lua_pushnumber( L, 1.0 );
 
   
 
    /* call the function */
 
    if ( lua_pcall( L, 2, 0, 0 ) != 0 )
 
    {
 
      goto bail_out;
 
    }
 
   
 
    /* we're done */
 
    lua_close( L );
 
    return 0;
 
  }

Apart from the nice API which makes a pleasure to work with Lua, you can configure the Lua build to change the number type (double is the default) and also change the allocator used by it at runtime.

It has a very well designed API

Check Lua’s C API. Write an C/C++ application that embeds Lua and I’m sure you’ll want that all APIs were as good.

It’s syntax is simple and easy to learn

Again, check the Lua manual and judge for yourself. It’s more verbose than C, but its similarities with BASIC and Pascal makes it easy to learn even for non-programmers.

It’s extensible

Lua comes with a set of libraries which add useful functions that can be called from Lua scripts, but it’s also very easy to make your own functions available to Lua scripts:

static int Add( lua_State* L )
 
  {
 
    /* get the first argument */
 
    double a = luaL_checknumber( L, 1 );
 
   
 
    /* get the second argument */
 
    double b = luaL_checknumber( L, 2 );
 
   
 
    /* push the result onto the stack */
 
    lua_pushnumber( L, a + b );
 
   
 
    /* tell the VM we're returning one result */
 
    return 1;
 
  }
 
   
 
  int main( void )
 
  {
 
    /* ... */
 
   
 
    /* push Add onto the Lua stack */
 
    lua_pushcfunction( L, Add );
 
   
 
    /* set it to a global */
 
    lua_setglobal( L, "Add" );
 
   
 
    /*
 
    now add is available to Lua scripts as a global function
 
    which can be called in a script:
 
   
 
    local a = add( 1, 2 ) -- sets a to 3
 
    */
 
   
 
    /* ... */
 
    return 0;
 
  }

It’s also easy to extend Lua with your own types via metatables. Lets see how we can make a simple structure available to Lua scripts:

typedef struct
 
  {
 
    double x, y;
 
  }
 
  Point;
 
   
 
  static int PointIndex( lua_State* L )
 
  {
 
    /* get a pointer to the Point structure */
 
    Point* point = (Point*)luaL_checkudata( L, 1, "Point" );
 
   
 
    /* get the key used to index the structure in the Lua script */
 
    size_t length;
 
    const char* key = lua_tolstring( L, 2, &length );
 
   
 
    /* if key is NULL it's not a string */
 
    if ( key != NULL && length == 1 )
 
    {
 
      if ( *key == 'x' )
 
      {
 
        /* key is "x", return the x field */
 
        lua_pushnumber( L, point->x );
 
        return 1;
 
      }
 
      else if ( *key == 'y' )
 
      {
 
        /* key is "y", return the y field */
 
        lua_pushnumber( L, point->y );
 
        return 1;
 
      }
 
    }
 
   
 
    /* key not found, raise an error */
 
    return luaL_error( L, "Field %s not found in structure Point", key );
 
  }
 
   
 
  static int PointNewIndex( lua_State* L )
 
  {
 
    /* get a pointer to the Point structure */
 
    Point* point = (Point*)luaL_checkudata( L, 1, "Point" );
 
   
 
    /* get the key used to index the structure in the Lua script */
 
    size_t length;
 
    const char* key = lua_tolstring( L, 2, &length );
 
   
 
    /* get the value the field must be set to */
 
    double value = luaL_checknumber( L, 3 );
 
   
 
    /* if key is NULL it's not a string */
 
    if ( key != NULL && length == 1 )
 
    {
 
      if ( *key == 'x' )
 
      {
 
        /* key is "x", set the x field */
 
        point->x = value;
 
        return 0;
 
      }
 
      else if ( *key == 'y' )
 
      {
 
        /* key is "y", set the y field */
 
        point->y = value;
 
        return 0;
 
      }
 
    }
 
   
 
    /* key not found, raise an error */
 
    return luaL_error( L, "Field %s not found in structure Point", key );
 
  }
 
   
 
  int main( void )
 
  {
 
    /* ... */
 
   
 
    /* get a global Lua function */
 
    lua_getglobal( L, "ZeroPoint" );
 
   
 
    /* push a point instance onto the Lua stack */
 
    Point* point = (Point*)lua_newuserdata( L, sizeof( Point ) );
 
   
 
    /* initialize its fields */
 
    point->x = 1.0;
 
    point->y = 2.0;
 
   
 
    /*
 
    create a new metatable for our Point structure on the Lua stack or use an
 
    already existing one
 
    */
 
    if ( luaL_newmetatable( L, "Point" ) != 0)
 
    {
 
      /* set its __index field. */
 
      lua_pushcfunction( L, PointIndex );
 
      lua_setfield(L, -2, "__index");
 
   
 
      /* set its __newindex field. */
 
      lua_pushcfunction(L, PointNewIndex );
 
      lua_setfield(L, -2, "__newindex" );
 
    }
 
   
 
    /* set the metatable for our Point instance */
 
    lua_setmetatable( L, -2 ); /* -2 is the element below the stack top */
 
   
 
    /*
 
    call a lua function that receives our point structure
 
    at this point the Lua stack is:
 
      top -> the ZeroPoint function
 
             the Point instance
 
    */
 
    if ( lua_pcall( L, 1, 0, 0 ) != 0 )
 
    {
 
      fprintf( stderr, "%s", lua_tostring( L, -1 ) );
 
      lua_close( L );
 
      return -1;
 
    }
 
   
 
    /*
 
    assuming ZeroPoint is
 
   
 
    function ZeroPoint( point )
 
      print( point.x, point.y ) -- uses __index
 
      point.x, point.y = 0, 0 -- uses __newindex
 
    end
 
   
 
    it'll print 1 and 2 and set out point to 0, 0
 
    */
 
    printf( "point = { %f, %f }\n", point->x, point->y );
 
   
 
    /* ... */
 
    return 0;
 
  }

There are also a number of tools that can parse annotated C/C++ code and generate C code to make functions, structures and classes available to Lua scripts, as well as Lua extensions that allows for bindings to be made during runtime.

It has a active mailing list

Lua has a mailing list where Lua experts, its authors and users exchange ideas, seek and give support and make announcements of Lua and Lua ports and extensions. The technical level of the posts is fairly high and participating in the mailing list is a great way to know more about the language.

Has a permissive license

Lua is released under the MIT license, what more could you ask for?

Conclusion

If you’re looking for a scripting language to prototype, expose engine functionality to gameplay, let users extend your game or even write entire games with it, take a look at Lua. Don’t be afraid, you’ll be in company.

Links