A while back @celsiusgs published a blog entry discussing using C++ polymorphism for pluggable AI in his upcoming game Drifter. In a nutshell, each ship in the game is controlled by a
ShipObject
that has the ship’s state as well as methods (“inputs”) to control the ship (throttle, steering, etc.). This class provies a process
method that updates ship state (presumably based on some sort of physical model and the current state and inputs). For player controlled ships, the ShipObject
inputs are driven by touch controls. For instance, when the player touches the throttle control the throttleControl
method(s) would be called (presumably).
This separation of the physical controls that respond to user touches from the ship controller makes it easy to plug in AI code to control NPC ships. For NPC ships, the
ShipController
object is assigned an AI
object that accesses the ShipController
to control the ship’s state. This AI
object implements a process
method that is called from the ShipObject
’s update
method once per game loop.
To make this as flexible as possible, @celsiusgs has created a generic
AI
class that provides a virtual process
method that is implemented by classes inheriting from this base class. This allows him to simply attache different AI
implementations to each NPC ship to get different behaviour.
The decoupled nature of this approach provides another benefit that @celsiusgs mentions: it provides an easy entry point for replacing C++ code with a scripting system.
I won’t get into a discussion here of the pros and cons of using an embedded scripting system. I like to think that there are more reasons supporting using one than reasons against it. For this appliction, however, there is one huge benefit that definitely makes it worth doing.
Implementing even simple AI is nontrivial and often involves a lot of iterations as you try to weed out “dumb” behavior. An NPC ship repeatedly bumping it’s “head” into an asteroid tends to spoil a games immersiveness. Executing test runs over and over again is particular painful on the iOS platform if the code under test needs to be compiled every run.
If the code is run from a script, however, these scripts can be read from a webserver every time they are executed, so the main program does not need to be rebuilt every run. In fact, if the code is written to support it, it may not even need to be restarted, but merely reset. This allows the AI coder to make changes to his/her scripts and rapidly test the effects.
By far the most popular embedded scripting language for game development is Lua. I won’t describe Lua in much detail here, instead I’ll refer the reader to the main Lua site. Suffice it to say, it has three properties that make it a good choice for embedding scripting in a game:
- It was designed as an embedded language from the ground up
- It is based on standard C/C++ code and is therefore runs on many platforms
- It is lightweight and very fast
In this three part blog entry I am going to demonstrate how to embed Lua into an iOS app, with particular emphasis on a system like the one in Drifter, albeit using much simpler game mechanics. If you want to follow along in Xcode the project can be cloned from github at git@github.com:indiejames/TestLua.git.
First things first, we need a game platform in which to embed our lua interpreter. For this demonstration, I will create a very simple sample program using a single view. I want to keep the main program as simple as possible to focus on the lua portion, so this program won’t have any OpenGL or other graphics and only a rudimentary user interface.
I’ll skip using storyboards or generating tests as these are outside the scope of this discussion.
At this point we have a simple single view application with the standard boilerplate code. Aside from the startup code and support files, this consists of just two Objective C classes,
AppDelegate
and ViewController
. These should be familar to anyone working in iOS, so I won’t discuss their core functionality here. If you don’t know how these work, check at one of the many resources related to iOS development.
I won’t be changing the
AppDelegate
class at all for this example so it is not shown here. All our changes to the boilerplate code will be done in ViewController
. The bare bones code for this class is shown here:
This is where we will load and invoke our Lua interpreter, but for now let’s set up our simple game mechanics. For this we will create a new class that we will call
ShipController
, which will manage the state of our ship and respond to inputs.
So now we have an empty class as shown here:
I want to keep the game mechanics simple so I can focus on the scripting, so I am going to create a game in which ships move in two dimensions. We will use the OpenGL convention of the y-axis pointing “up”. All ships will have an intrinsic “speed” which determines how far they move in response to inputs. The player will have four buttons (Left, Right, Up, Down) that can be pressed to control the ship. When the player presses one of the buttons, the ship will move a certain number of units in that direction, where this number is given by the ship’s intrinsic speed. For instance, if the player’s ship has a speed of four, pressing the Up button twice will increase the
y
value of the ship’s postion by eight units. Note that we won’t be modeling velocity or accelaration here - the ship simply changes its position based on its inputs and its intrinsic speed.
We add the following code to the ShipController.h file to add instance variables to hold the state of ship, along with property declarations to make them available to other classes.
We have variables
x
and y
to store our ship’s position, speed
which defines the instrinsic speed of the ship (NOT its velocity) and a name
member by which we will distinguish different ships in our output.
Our property declarations will allow us to output the position of the ships by name periodically so we may observe their behaviour. We will not be generating any graphical output in this example.
In addition to the state for our ships, we need to provide methods to represent the “inputs” with which we can control it. We add the following to the header file:
The first method is our init method and it sets the initial state (x,y) for a ship as well as it’s intrinsic speed and its name. The next four methods are the “input” methods that will be called whenever someone presses one of the buttons. Notice that there is no
process
method. Because we are using such a simple physics model, I am going to simply update the ships position directly every time one of the button press methods are called. A more sophisticated simulation would implement a process
method ala Drifter to update the ships state once every game loop.
The implementation for these methods are shown here:
The
init
method sets the initial state (x,y) the only model parameter, speed
, and the name for the ship. The button press methods log the action and direcctly manipulate the ship’s state.
Now we modify our
ViewController
to instantiate a ShipController
for the player’s ship. First we import the header for or ShipController
class, then declare a global variable to hold a pointer to our player’s ship controller. Obviously using a global here is bad practice, but we do so now to keep things simple. Finally we instantiate the global in the viewDidLoad
method of the ViewController
.
Now that we have our ship’s controller we need to add a way for the user to manipulate it. To this end we add four simple buttons to our interface and wire them to our view controller. First we add four actions to the view controller (one for each button).
These actions simply delegate to the
ShipController
class by calling the corresponding pressButton
actions. We have also added an NSTimer
to our ViewController
. This will be used later to set up the game loop.
Now we use Interface Builder to add four buttons two our view and connect them to our actions.
We can test our game by executing it and pressing a few buttons. The output from this run is given here:
2012-02-04 22:27:46.348 TestLua[24376:f803] Top button pressed for ship player_ship
2012-02-04 22:27:47.028 TestLua[24376:f803] Right button pressed for ship player_ship
2012-02-04 22:27:47.676 TestLua[24376:f803] Bottom button pressed for ship player_ship
2012-02-04 22:27:48.260 TestLua[24376:f803] Left button pressed for ship player_ship
The last thing we’ll do before adding our Lua interpreter is to set up a game loop. We simply initialize the
timer
in the viewDidLoad
method of our ViewController
:
This timer will execute the
runLoop
method every second. Not exactly blazing, but since we aren’t going to be doing graphics, this is more than enough.
The
runLoop
method is given here:
This simply writes out he position of the player’s ship every time it executes. So now if we run the game and press the up button a couple of times, we get the following:
2012-02-04 22:52:08.802 TestLua[25770:f803] Player ship at (100.000000,100.000000)
2012-02-04 22:52:09.508 TestLua[25770:f803] Top button pressed for ship player_ship
2012-02-04 22:52:09.801 TestLua[25770:f803] Player ship at (100.000000,102.000000)
2012-02-04 22:52:10.166 TestLua[25770:f803] Top button pressed for ship player_ship
2012-02-04 22:52:10.801 TestLua[25770:f803] Player ship at (100.000000,104.000000)
2012-02-04 22:52:11.801 TestLua[25770:f803] Player ship at (100.000000,104.000000)
2012-02-04 22:52:12.802 TestLua[25770:f803] Player ship at (100.000000,104.000000)
2012-02-04 22:52:13.801 TestLua[25770:f803] Player ship at (100.000000,104.000000)
Now we have our (very simple) game working to the point that we can steer our ship around using our buttons. In Part II we will get to the heart of the matter and add our Lua interpreter and some bindings that will allow us to use Lua scripts to interact with our game. Finally, in part III, we will wrap this up in a new Lua type that will allow us to easily control NPC ships in our scripts.
In part I of this three part post, I introduced a simple game framework to which we are going to add Lua scripting. Now we are going to get right to it and cover all the steps necesary to get Lua scripting working. This will include embedding the Lua interpreter in the Xcode project, running Lua scripts from Objective C code, and accessing game state (ship position) in Lua scripts via a C function we will write and bind to Lua.
The first thing we need to do is grab a copy of the Lua source here. I’m using version 5.1. Version 5.2 was just released at the end of 2011, but the C binding system has changed somewhat so I am not using it here.
Once you have downloaded and untarred the archive, rename the
src
directory lua
. Then drag the lua
directory from Finder into your Xcode project. Be sure to uncheck the “create external build system box” and have Xcode copy the files and create a group:
Now open the lua group and delete the Makefile, lua.c, and luac.c files. We wont be using the Makefile to build the source and we don’t need lua.c or luac.c as these are the code for the standalone interpreter.
Now build the project using Xcode. It should build without issues. Congratulations! You’ve just embedded Lua in your iOS project! Of course, it isn’t actually doing anything yet, so let’s get to that.
One of the nice things about embedded Lua is that you can choose just how tightly you want to bind your code to Lua. Lua provides a C API that lets you control this. In the simplest case, you can call Lua scripts from C and get the results back. At the next level, you can have your Lua scripts invoke C functions that you bind to Lua. Finally, you can define new Lua types that not only invoke C functions, but instantiate C++/Objective C objects and delegate processing to them. We will look at each of these levels in turn.
First, let’s just invoke the embedded interpreter to execute a simple Lua script. I won’t cover Lua syntax here as it is oustide the scope of this blog post, but suffice it to say that its syntax is similar enough to C that the reader should have no problem following along. Please refer to The Lua Programming Language or another reference for a comprehensive introduction.
As I mentioned, Lua provides a C API for interacting with the interpreter. Actually, it provides two APIs, the basic API and the auxilary API. The auxilary API adds convenience methods to the basic API that make it a bit easier to use.
We need to add a few imports and a declaration to our
ViewController.h
file.
One of the nice things about the Lua code is that it’s designed to be reentrant. Therefore it maintains no internal state. Instead, the interpreter state is maintained externally in a C struct called, appropriately,
lua_State
. The main program creates these and passes them to the interpreter with every call in the APIs. For this application, we will only need one instance and we add it as an instance variable for our ViewController
. Ideally we would probably create a separate class to handle all of our Lua interaction, but in this example we are simply going to do everything in the ViewController
.
Add the following lines to the
viewDidLoad
method of ViewController.m
before the call that creates the NSTimer
:
All of the methods in the basic API have names that start with
lua_
, while all methods in the auxillary API have names that start with luaL_
. The first thing we need to do is inovke luaL_newstate
which instantiates a lua state structure for us and returns a pointer to it. The next call is to luaL_openlibs
, which opens all the basic Lua libraries - we will use this to inject our own C methods into Lua later.
The next call to
lua_settop
brings up a very important point that needs to be made. All passing of data from C to Lua and from Lua to C is done via a stack. When C code invokes a Lua scripted function it passes the arguments to the Lua function on the stack. It also reads values returned by the function from the stack.
Similarly, when a Lua script invokes a bound C function, it passes its arguments on the stack and reads its return values from the stack. The call to
lua_settop
simply tells Lua that the stack contains zero values, i.e., it’s empty.
The next call to
luaL_loadstring
compiles our one line “Hello, world.” Lua script and returns a nonzero error code if something went wrong. We check this code and use luaL_error
to get a more meaningful error message if something went wrong. For now we are just providing the script in a hard-coded string. Later we will read this script from a file.
Finally, we execute the Lua script using
lua_pcall
. The p
in lua_pcall
stands for protected, which essentially means that if something goes wrong the interpreter will trap the error and return an error code instead of propagating the error upwards.
After adding these lines, run the program and you should see output like this:
Hello, world.
2012-02-05 15:23:07.899 TestLua[29603:f803] Player ship at (100.000000,100.000000)
2012-02-05 15:23:08.899 TestLua[29603:f803] Player ship at (100.000000,100.000000)
More progress! We have just run our first Lua code from within our iOS project! Now let’s do something a bit more useful. We are going to demonstrate how to bind a C method to Lua so we can call it from our scripts.
Let’s extend Lua so that it knows where our player ship is. Add the following function to
ViewController.m
.
This C funciton is declared
static
so it cannot be used outside of our ViewController
and therefore does not need to be declared in a header. Remember, this is NOT an instance method. It implements the basic prototype for C functions that are bound to Lua - it takes a single lua_State
argument and returns an int
. This method does two things; it pushes the ship’s x and y coordinates on the stack and returns the number of items it has placed on the stack, 2. Objective C 2.0 properties work even inside a C function, so we can access the ship’s position using its properties x and y.
Now we have a function that will return the ship’s position to Lua via the stack, but we must first bind it to Lua before we can use it in a script. There are a couple of different ways to do this, but the most useful one if you are going to bind many functions (and we will) is to put them in an array and pass this array to the
luaL_register
function. So we add the following code to ViewController.m
.
We declare an array of
struct luaL_Reg
which are structures containing a function to be bound and a name to which to bind it. This array is terminated by an entry with NULL
values. We pass this array to the luaL_register
function (macro, really) along with a lua_State
and a name for the table that will contain the functions in the array.
We need to call the
luaopen_mylib
function before we run our script. The easy way to do that is to add it to the list of libs that automatically get loaded when the Lua interpreter starts. This can be done by modifying the linit.c
file in our lua directory like so:
The only changes we have made here are to add
{"ship", luaopen_mylib},
to the lualibs
array and to add the extern int luaopen_mylib (lua_State *L)
declaration so the linker can do its thing.
Now we are going to modify our Lua script to call our new method. At the same time, we will load our script from a file instead of a hard-coded string. First, let’s make the changes to
ViewController.m
. Replace the call to luaL_loadstring
with the following.
This will load and execute the script in the file
ship.lua
. Now let’s create this file.
This file defines a single lua function which will use our bound player_ship_position method to get the x,y position of the ship and print it out.
Now we need to execute this method in our run loop:
Running the program and pushing the up button a couple of times produces the following:
2012-02-05 20:09:52.370 TestLua[32429:f803] Player ship at (100.000000,100.000000)
Lua says player ship is at (100.000000, 100.000000)
2012-02-05 20:09:52.892 TestLua[32429:f803] Top button pressed for ship player_ship
2012-02-05 20:09:53.310 TestLua[32429:f803] Top button pressed for ship player_ship
2012-02-05 20:09:53.370 TestLua[32429:f803] Player ship at (100.000000,104.000000)
Lua says player ship is at (100.000000, 104.000000)
2012-02-05 20:09:54.370 TestLua[32429:f803] Player ship at (100.000000,104.000000)
Lua says player ship is at (100.000000, 104.000000)
Now we’re getting somewhere! We can access our player ship’s state from within a Lua script, which opens up a lot of possibilities. We could create an entire C API that would let us manipulate our ship by calling methods on our
ShipController
instances. In fact, we will do something similar to this in Part III, but we will wrap it into a nice object oriented layer that will make our scripting cleaner.
In part I of this post, we created a simple iOS game in which to embed Lua scripting. In part II we embedded the Lua interpreter and demonstrated calling Lua scripts from our game. We also added some simple interaction by enabling our Lua scripts to access the state of our player ship (x,y position).
In this final part, we are going to build on this functionality to expose the entire ship control to Lua so that our scripts can control our NPC ships.
In part I of this post we created the
ShipController
class to allow our code access to the state of a ship (position) as well as to manipulate the ships controls. In part II we used a Lua script to access the ship’s state. Now we are going to make the full ShipController
interface available to our Lua scripts.
We will begin by creating delagate functions for the rest of the methods in our
ShipController
. Add the following code to ViewController.m
:
These five functions are our delegates that implement the standard Lua C function prototype mentioned in part II. Each of these follows a standard format. First they pull a pointer off the stack and assign it to a local variable. This pointer is a
ShipController
pointer for the ship that is to be controlled. After that they use the local variable to perform an action, or, in the case of get_ship_position
, to retrieve information. Finally, they push results on the stack where necessary and return the number of results pushed. The only really new thing here is the call to lua_touserdata
at the beginning of each method. So what does this do?
There are essentially two different ways to pass non-primitive data to Lua. The first way lets Lua manage the memory for this data. We can define a struct to hold our data elements (including pointers) and ask Lua to allocate and manage the memory for instances of this struct, which we then fill in with our data. This approach is called user data. User data has the advantage of having Lua manage the memory, but its implementation is slightly more complicated.
The second approach is to simply pass pointers to Lua for the objects we create and manage in the main game code. This approach is known as light user data, and is the one we will use here.
Next we need to add these functions to our Lua library. This is simply a matter of adding entries for each to our library array.
Now all of these methods are available to our lua code. Before we can use them, we need to create another ship. Add the following code at the top of
ViewController.m
just below the allocation of our player ship:
And add an allocation in
viewDidLoad
just below the one for the player ship:
Our enemy ship will originate at (x,y) = (105, 102), or slightly offset from our player ship.
Next we need to create a simple AI script for our enemy ship. Create a new file called enemy_ship.lua and add the following:
This script declares a sigle function,
process
, which takes a single argument, a pointer to a ShipController
. The function does nothing with this pointer directly, it simply passes it to the functions we bound to our ship
library. The “AI” here is about as primitive as can be; it simply causes the enemy ship to move a bit closer to the player ship by calling the appropriate “press_*_button” functions.
The only things left to do are to load the new script and modify our game loop to call this lua function. The first is simply a matter of changing the name of the loaded file.
`
While we are at it, lets modify our output to make use of the
name
property we added to ShipController
.
This method uses the
lua_getglobal
function, which simply retrieves the address for the process
symbol (our Lua function name) and puts it on the stack. Then it pushes our pointer to our ShipController
for our enemy ship on the stack. Finally, it tells lua to call the function on the stack, using one argument - the pointer we pushed on the stack.
Now if we run our game we get the following output:
2012-02-06 20:57:30.609 TestLua[37090:f803] Left button pressed for ship enemy_ship
2012-02-06 20:57:30.609 TestLua[37090:f803] Bottom button pressed for ship enemy_ship
2012-02-06 20:57:30.610 TestLua[37090:f803] player_ship at (100.000000,100.000000)
2012-02-06 20:57:30.610 TestLua[37090:f803] enemy_ship ship at (104.000000,101.000000)
2012-02-06 20:57:31.608 TestLua[37090:f803] Left button pressed for ship enemy_ship
2012-02-06 20:57:31.609 TestLua[37090:f803] Bottom button pressed for ship enemy_ship
2012-02-06 20:57:31.609 TestLua[37090:f803] player_ship at (100.000000,100.000000)
2012-02-06 20:57:31.609 TestLua[37090:f803] enemy_ship ship at (103.000000,100.000000)
2012-02-06 20:57:32.608 TestLua[37090:f803] Left button pressed for ship enemy_ship
2012-02-06 20:57:32.609 TestLua[37090:f803] player_ship at (100.000000,100.000000)
2012-02-06 20:57:32.609 TestLua[37090:f803] enemy_ship ship at (102.000000,100.000000)
2012-02-06 20:57:33.608 TestLua[37090:f803] Left button pressed for ship enemy_ship
2012-02-06 20:57:33.609 TestLua[37090:f803] player_ship at (100.000000,100.000000)
2012-02-06 20:57:33.609 TestLua[37090:f803] enemy_ship ship at (101.000000,100.000000)
2012-02-06 20:57:34.608 TestLua[37090:f803] Left button pressed for ship enemy_ship
2012-02-06 20:57:34.609 TestLua[37090:f803] player_ship at (100.000000,100.000000)
2012-02-06 20:57:34.609 TestLua[37090:f803] enemy_ship ship at (100.000000,100.000000)
2012-02-06 20:57:35.609 TestLua[37090:f803] player_ship at (100.000000,100.000000)
2012-02-06 20:57:35.609 TestLua[37090:f803] enemy_ship ship at (100.000000,100.000000)
So there you have it. Our enemy ship controlled by a Lua script. We see the enemy ship script frantically pressing buttons to try to move closer to our ship. Not exactly the brightest AI in the world, but enough to get started. Along with better AI, there are some refinements that could be added. For one thing, constantly passing in the pointer to our enemy ship’s
ShipController
seems a bit cheesy. It would be better if we could wrap it up in a nice Lua object that our scripts could use, and we can. But that is the subject for another post.