Lua for scripting NPC behaviour

With character movement looking good, now it’s time to give the non-player-characters (NPCs in role-playing game parlance) some behaviour.

I have the idea that monkeys might chase after bananas, foxes after chickens, etc.  And what platform game would be complete without animals mindlessly going back and forth patrolling their platform?

After reading this post at Stoked Software, I like the idea of using Lua to script NPC behaviour, even though I’m sure the simple behaviours above could be much more easily done without it.  I downloaded Lua following the instructions in the second part of that post, and added it to my project by renaming the src folder lua, and dragging it into my XCode project. (Unlike the post, I downloaded Lua 5.2.1, not 5.1.)

Note this is the bare bones DIY approach, not using any existing wrappers like Wax or Corona, which are nicely described at Lua Nova. I’ll try it this way until I get frustrated.

Some initial set up decisions:

  • I decided to include the header files directly into the GameModel, since we only want one Lua state.  I was originally thinking of having one per animal.
  • Whereas the post adds an instance variable lua_State *l, I am adding a @property (readwrite) lua_State *luaState.
  • Also, to keep the NPC control separate from the rest of my code (in case I decide one day to use something other than Lua), I have added an NPC category to GameElement.

Some hurdles:

  • I get lots of “Undefined symbols for architecture i386:” errors, starting with one for luaL_error, the first Lua call I was trying to make. I posted a question on stack overflow which quickly sorted this out: it is because Box2D is in C++, but Lua is in C.  So you need to wrap the Lua includes with an extern "C" command. (See stack overflow for the precise solution.) In fact you’ll also need to wrap all the upcoming C code in this.
  • I get the error “Use of undeclared identifier ‘self‘” when the static C function which communicates with Lua tries to access the GameElement self. This is OK, the C code doesn’t have any concept of self; it means you have to pass the relevant object from Lua to C. Fortunately, the Stoked Software blog explains how to do this in Part 3, for enemy ships.
  • ARC – I have used __bridge everywhere so there is no transfer of ownership, would appreciate any thoughts on whether that’s correct.
  • Lua 5.2 does not use luaL_register, but instead luaL_setfuncs.  There’s a good discussion on the Lua users wiki, but in the end I could not make this work, so had to go back to Lua 5.1. Any advice on how to apply this to Lua 5.2 would be gratefully received.
  • The Stoked Software blog gives great examples of how to send position data from Objective C to the Lua script, and how to get the Lua script to trigger methods in Objective C so long as the only parameter is a single object.  However, I want to set a target point using Lua.  I have stumbled upon one way to do this, hinted at by the Lua users wiki, using luaL_checknumber, e.g.
    static int setTarget(lua_State *luaState) {
        //
        // Parameters: The GameElement whose target you want to set
        //             The x-coordinate of the target
        //             The y-coordinate of the target
        //
        // Returns: nothing
        //
        GameElement *element = (__bridge GameElement *)lua_touserdata(luaState, 1);
        float x = luaL_checknumber(luaState, 2);
        float y = luaL_checknumber(luaState, 3);
        NSLog(@"target (%6.2f, %6.2f) %@ %p",x,y, element.appearName, element);
        [element touchLocation:Point3DMake(x,y,0.)];
        return 0;
    }

    and then from the Lua script, it’s just:

    function process(gameElement)
      game.setTarget(gameElement, 1.1, 0.5)
    end

    I have no idea if this is a good way to do it, but it works.

     

With those hurdles surmounted, I can now use Lua to script my NPC behaviour.  I just need to think what that should be…

One last note – I see that the license for Lua requests that users give Lua credit (see the download page).