Anti-Alias.Me

LuaEngine 2: Lua, meet Objective-C.

by on Feb.19, 2010, under Uncategorized

This post is the logical sequel to my previous post on Lua. In the previous post I went over the basics of exposing functions and data from C to Lua, and vice versus. In this post, I want to expand on that by exploring the possibilities of exposing full Objective-C objects to our Lua runtime.

The direction that I’m taking these posts is from the perspective of a game developer, wanting to expose some of their native code to scripters in order to quickly develop features of their game. The Lua runtime is super small, so even using it to store configuration data is not a huge waste of time and space though using it to control actual gameplay is a much more interesting usage I must admit.

In this particular post, I’d like to go over what we need to do in order to expose an obj-c object to the Lua runtime, as well as set up the stage for a means to limit the functionality that the Lua runtime has access to. My reasoning for this is to provide an engine developer with a safety net to stop scripters from making use of every function available to an object. By providing this capability developers won’t need to create an extra “Script Object” layer that ultimately gets exposed to the Lua runtime, and can instead expose the objects closest to the inner workings of the game.

LuaEngine.h/.m

All of the code for this post will be in the same class, LuaEngine. Pretty much everything for the rest of this series of posts will be in here, all nice and contained. The only time we’ll venture off into other classes, is when we actually expose an object.

First, we’ll obviously want to re-create the functionality that we had in the last post. The ability to expose basic data, and basic functions to the Lua runtime from our obj-c code. To do this, I’ve created some basic messages on the LuaEngine class, as follows:

-(void)registerFunction:(lua_CFunction)func withName:(NSString*)name;
-(void)setGlobal:(NSString*)name toNumber:(NSNumber*)number;
-(int)doFile:(NSString*)file;
-(int)callFunction:(NSString*)func;

The code for these functions can be found here, and I won’t spend too much time on them, except for the setGlobal:toNumber, which might need a little bit of explanation. Here are the two functions involved:

void lua_pushnsnumber(lua_State *l, NSNumber *number) {
   switch ([number objCType][0]) {
      case _C_FLT:
      case _C_DBL:
         lua_pushnumber(l, [number doubleValue]);
         break;
      case _C_INT:
      case _C_LNG:
      case _C_ULNG:
      case _C_USHT:
      case _C_SHT:
         lua_pushinteger(l, [number longValue]);
         break;
   }
} 
 
-(void)setGlobal:(NSString*)name toNumber:(NSNumber*)number {
	lua_pushnsnumber(_luaState, number);
	lua_setglobal(_luaState, [name UTF8String]);
}

The part that might need a little bit of explanation is the lua_pushnsnumber function, and the objCType message at line 2. Basically what that is doing is asking the NSNumber object what the runtime type is for the number it is representing. The objective-c runtime keeps track of data types using a single character (or multi-character in some situations, like blocks) value for each primitive type (int, float, id, SEL, char, etc.). We’ll be seeing these constants again, but for our purposes here, we’re using the Lua C API functions lua_pushnumber and lua_pushinteger depending on the data type that the NSNumber has. Using lua_pushnumber if the data is a float, or double, and lua_pushinteger if the data is an int, long, unsigned long, unsigned short, or short.

By using the runtime data when/where we can, we can help to generalize our code so we don’t have to keep writing specific implementations. For instance, the message setGlobal:toNumber is a lot nicer than having to write setGlobal:toFloat, setGlobal:toInteger, setGlobal:toDouble, etc.

Now that we have those methods defined, we’re basically right where we left off last time. To expose the same data that we did in the last post, we’d use the following code:

LuaEngine luaEngine = [[LuaEngine alloc] init]; // Create our Lua stack
[luaEngine setGlobal:@"MAX_WIDTH" toNumber:[NSNumber numberWithFloat:1.0f]];
[luaEngine setGlobal:@"MIN_WIDTH" toNumber:[NSNumber numberWithFloat:-1.0f]];
// And so on..

Object Oriented Exposure

Now that we’re back to where we ended last post, lets consider what we need to do in order to expose an object to Lua.

Lua Syntax

First thing is first, we need to figure out how we would provide access to an object from Lua. Since Lua doesn’t have a typical object-oriented functionality, we can’t just expose an object and tell Lua that it is an object. What Lua does have though are tables, and a very extensible method for adding functionality to tables.

Lua’s tables are of the typical hash-table variety. So syntactically in Lua one would probably use them like so,

someTable = {} -- Initialize table
someTable["key"] = value -- Set value to the table entry 'key'

Which is a pretty ugly way to access objects and their members. Luckily, Lua also provides a syntactical sugar which allows us to use tables like so:

someTable = {} -- Initialize table
someTable.key = value -- Set value to the table entry 'key'

This, is starting to look MUCH better.

So we know in Lua, functions are first class citizens. Meaning, we can pass a function around just like it was a normal piece of data. So, we could do something like this:

function someFunc()
   print "Hi"
end
someTable = {} -- initialize table
someTable.blah = someFunc
someTable.blah()

..and the output we should get is a simple, “Hi”.

So, what we’ve just proved, is that Lua knows how to treat functions as data. So if we’re able to expose our object as a table, and somehow let Lua know each of the functions and data members that we want to provide, via entries in the table, we can give Lua access to C functions, and data via the table.

Now, remember how I said Lua makes extending tables super easy? Every time a value is requested from a table, Lua triggers a meta method called “index” which will ask the table “what is the data associated with this key?”. Normally, the standard Lua “index” method is used, and data is found using the built in hashing algorithm, and returned nice and smooth. Lua knows to call this internal index method by looking at the table’s “metatable”. The metatable is another table, that keeps track of what functions are available for a given table. Think of it like a Lua version of C++’s vtable, only there isn’t any inheritance, only one table.

We can play with this using straight Lua like this:

1
2
3
4
5
6
7
8
9
function lookup(table, key)
   print("Lookup: " .. key)
   return "BEWM"
end
mt = {}              -- Initialize our metatable
mt.__index = lookup  -- Set a new function for the "index" metamethod
t = {}               -- Create a new empty table
setmetatable(t, mt)  -- Set the metatable for t to mt
print(t.asdf)        -- Try to find a value in t...

First I’ve defined a new function called lookup, note the two parameters. The first, is a table, which represents the table the lookup is being requested for. The second parameter, is the value for the key. Keep in mind, that a key doesn’t necessarily have to be a string value, it can be anything, including a function. For our test purposes though, I’m assuming it’s a string.

Next, I create a new table, mt, which represents our metatable. I set the “__index” value to our lookup function. Remember that when a value is requested for a table entry, the “index” metamethod is called. Metamethods like index, are preceded by two underscores when referencing the actual table entry for their functions.

After setting the index function, I create a new empty table, and call setmetatable which is a Lua function that replaces the current metatable for a table, with one provided. So, I’ve set the metatable for t to the table mt.

The last line, is where the magic comes together. In attempting to print out the value for “asdf” in the table t, I trigger the index metamethod on t, which is now wired up to my lookup function. When lookup is called, it’s given a reference to t and the key that it wants the value for. So, when the code is run, we get the output:

Lookup: asdf
BEWM

Which shows the output from lookup, telling us that the value for “asdf” was requested. As well as the output “BEWM” which, as far as Lua knows, is the value for “asdf” in the table t.

So now it should be pretty clear that if we can associate an object with a Lua table, then we can hijack the metamethods in order to return appropriate values for whatever members are requested by the Lua script.

The Almighty User Data

Now, our last challenge, exposing the object itself comes pretty quickly to realization with the usage of Lua’s user data type.

Lua’s userdata allows us to allocate memory in the Lua runtime, and store a value there. For all intents and purposes, that new data we store is Lua data. Which means it can have its own metatable. It can also be collected via the Lua’s garbage collection. Super.

Bringing it all together..

Lets introduce a new message in LuaEngine..

-(void)setGlobal:(NSString*)name toObject:(NSObject*)obj;

So, we need a userdata for each object, and ideally a metatable for each object type. Though, we can really get by with only a single metatable for our entire bridge, since we’ll be exposing objects the same way no matter the object. So, lets start out with the following code,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int luaEngine_methodcall(lua_State *l) { 
}
 
static int luaEngine_methodLookup(lua_State *l) {
	const char *s = luaL_checkstring(l, 2); 
	lua_pushstring(l, s);
	lua_pushcclosure(l, &luaEngine_methodcall, 1);
	return 1;
}           
 
-(void)setGlobal:(NSString*)name toObject:(NSObject*)obj {
   	if (luaL_newmetatable(_luaState, "LuaEngineMetaTable") != 0) {
		lua_pushstring(_luaState, "__index");
		lua_pushcfunction(_luaState, &luaEngine_methodLookup);
		lua_settable(_luaState, -3);
	}
	void **data = lua_newuserdata(_luaState, sizeof(id*));
	*data = obj;	
	luaL_getmetatable(_luaState, "LuaEngineMetaTable");
	lua_setmetatable(_luaState, -2);
	lua_setglobal(_luaState, [name UTF8String]);     
}

When we come into setGlobal:toObject the first thing we try to do is create the metatable. If the metatable exists, the call to luaL_newmetatable does nothing and we continue on. If it doesn’t exist however, we create the metatable, and set the metamethod “index” to a C function I’ve defined called luaEngine_methodLookup.

Then we create our new userdata, and set its metatable to the metatable we just created. Once that’s done, our userdata is still on the stack, so we simply call lua_setglobal to make it a global variable with the given name, name.

And we’re done. Kinda.

As you can see there are two other functions that I haven’t explained, but by now they should be pretty self explanatory in their purpose if not in their implementation.

If you recall the Lua function lookup I defined earlier, you pretty much know the purpose of luaEngine_methodLookup. Just like our Lua function, this function’s task is to supply Lua with the value of the requested table entry, for the supplied table. Obviously the only way Lua is going to call this C function is if we do an entry lookup on one of our userdata objects, so we have a pretty good idea that we’ll be wanting to send a message to the object.

That assumption is the reason luaEngine_methodLookup is so short. All it needs to do is tell Lua, “Hey, it’s a function! And here’s the address.”

You might be asking, “If that’s all it is doing what is this pushstring on line 6?? And what is it pushing a closure for??”. My astute reader, you are correct. We aren’t just returning the address of a function and calling it a day. In order to get the actual address of the function we need to call, we need to know the selector for the Objective-C message.

Note: For the purposes of this post (and this will change next post), I’m making the assumption that our Objective-C object has simple functions. Stuff like,

1
2
3
4
5
  // We can use these two..
  -(float)getX;
  -(void)setX:(float)x;
  // But not this one..
  -(void)setX:(float)x andY:(float)y;

I’ll explain why, and solve the problem next post, but for now we use the same name as the Lua value being requested for our Obj-C selector.

Since, we’re not 100% sure what selector is actually being requested (think: the # of parameters is unknown at this point, etc.) I’m telling lua to use the same function address for every function call. In order to determine what selector to use when the function is actually called, we need to provide that function with the information we do know. In order to do that, we use up-values and closures.

Up-values are similar to C’s static local variables, for a function. In Lua, a function with up-values, is a closure. So we tell Lua that the value it is requesting is a closure, and we return the function we want it to call (luaEngine_methodcall), but before doing that we set an up-value for the function to the name of the value requested (our ‘selector’). This way, when luaEngine_methodcall gets called, it will have a copy of the name requested by Lua making the job of finding what message we actually want to call, all the more easier (read: possible).

I’m going to end here, simply because this post has been sitting in my draft bin for months, and I’d like to get it out in the hopes it will motivate me to get the next one out all the sooner. :) Next post, we’ll solve the selector problem, and show some decent demo code which could be pieced together from what is in this post.

As always, comments are welcome.. unless you’re selling drugs, or advertising porn.. then not so much.

1 Comment more...

New Venue…Continued.

by on Jan.15, 2010, under Uncategorized

So this is the new site. :D

I’ve copied over a few of the posts that I had on the previous blog, and am in the process of polishing them up. That may take some time, as most of them were about projects I was working on a long time ago. So I’ll need to pull the code back out, and see where I was when I left off.

For the immediate future however, I’ll be using this site to help myself with a literature review I need to conduct for my honors thesis. I will basically be summarizing and discussing articles that I read to prepare for the work involved with my thesis. So, it will not only help me get work done, but also might be of benefit to anyone reading this who is interested in the topics I’ll be exploring (primarily medical visualization, mobile graphics research, and volume rendering).

I should be kicking off the first summary in the next day or two, I’m preparing for the start of the semester so it might be a bumpy start. But, I need to get this done, so it will happen.  As I get some free time, I’ll finish off the articles I have in queue, and we can explore some cool topics together. :) I have one more post about Lua and Objective-C lined up, which is why I brought over the previous one. I have a nearly complete Lua bridge implemented, that follows a pretty solid game-scripting guideline that I’ve been thinking about. So that will be fun to finally get out in the open.

So, wish me luck, and lets make this happen for realz this time. :D

As always, feel free to comment, and discuss, that’s what this is here for. :)

-glitch

Leave a Comment more...

LuaEngine: Using Lua With Objective-C

by on Aug.10, 2009, under Lua, Objective-C

In this series of posts, I want to explore the, already well covered, topic of using Lua for game scripting. My direction will include Objective-C as the host language, though the beginning code will stick to C. The end goal of these posts, is to implement a bridge between Lua and Objective-C that will allow us to expose obj-c objects to Lua script, without a large amount of “setup” code.

I’m going to make the assumption that anyone reading this has already gotten Lua compiled for their target platform, and can jump right into embedding Lua into a simple application.

The Basics

The first step to creating a generic Lua engine in Objective-C is introducing yourself to how Lua’s C API works and how we can use it to share data between script and native code. So, here is the bare minimum we need to bring Lua into our app:

int main(int argc, char **argv) {
   if (argc != 2) {
      printf("Usage: %s \n", argv[0]);
      return 1;
   }
   lua_State *lua = lua_open();
   luaL_openlib(lua); // Open standard libs (math, io, os, etc)
   luaL_dofile(lua, argv[1]); // Run the script supplied by the user
   lua_close(lua); // Shut down our lua vm.
}

The above code will:

  • Start a Lua VM (lua_open)
  • Initialize standard libs (luaL_openlib)
  • Run the Lua script supplied by the user. (luaL_dofile)
  • And finally, terminate the Lua VM. (lua_close)

This can be tested by providing a Lua script on the command line, like:

./luatest test.lua

So, lets pretend that we wanted to expose functionality to Lua that would allow us to write Lua script that would manage the location of a game object. For simplicity, lets call that object a Box. The Box is a simple quad that we’ll render in our game. The goal, will be to have the game code remain static, while we implement various movement algorithms for our Box.

Sharing Data

Our first phase will be to expose global values that represent the X & Y coordinates of the Box. Through those global variables we can just set the coordinates that we want, and the render loop of our game can read them in for translation. So, building onto the above code we could add:

static float box_x = 0.0f;
static float box_y = 0.0f;
int initializeGlobals(lua_State *l) {
   lua_pushnumber(l, box_x);
   lua_setglobal(l, "BoxX");
   lua_pushnumber(l, box_y);
   lua_setglobal(l, "BoxY");
}

I’ve added two global variables (box_x & box_y). Please note, I am in no way saying this is the best way to implement this, but it certainly is the easiest for this small example.

With those two global variables defined, the function initializeGlobals, when called, will take the supplied Lua VM and push the values of both global variables into the Lua environment. It does this by first pushing box_x‘s value onto the Lua stack. Then, it calls lua_setglobal and supplies the name “BoxX”. What this does is, pops the value, of box_x that we pushed onto the stack, off and adds it to the global registry under the name “BoxX”. This will allow any Lua script which runs from now on, to access a global variable named, BoxX. Initially, the value will be our initial value of box_x, but the Lua script will be able to change that as it wishes. We’ll also have to implement code later on to read back the current value of that global variable if we ever want to do anything else with it.

Now, all we have to do is make a call to initializeGlobals inside our main function from above.

luaL_openlib(lua);
// Begin new code
initializeGlobals(lua);
// End new code
luaL_dofile(lua, argv[1]);

To test it out we can write a quick Lua script like the following:

-- Testing global variables
print("X: ", BoxX, " Y: ", BoxY)

Which should give an output like:

X:      0        Y:     0

Ok, so now we can initialize our BoxX and BoxY variables so our code can use them. But, like I said before, we now need to write a little more code so we can read back their values. The code uses a very similar pattern to how we set the values, but as expected, a bit in reverse:

void readGlobals(lua_State *l) {
   lua_getglobal(l, "BoxX");
   box_x = lua_tonumber(l, 1);
   lua_getglobal(l, "BoxY");
   box_y = lua_tonumber(l, 2);
   lua_pop(l, 2);
   printf("x = %.3f  y = %.3f\n", box_x, box_y);
}

So, instead of pushing our values on, and telling Lua that we want to set new globals, we now tell Lua what globals we want to get and Lua pushes their values onto the stack. So, we start off by getting the value of BoxX by calling lua_getglobal. Once that’s done, the value of BoxX will be on the top of the stack for us. Once the value is on the stack, we can actually use it to do anything we want, like use it for a Lua function’s parameter, or any number of things. But instead, we’re simply going to copy the value from the stack, using lua_tonumber, and specifying stack index 1. Something to take note of, is the fact that lua_tonumber does not pop the value off the stack. So we’re left to clean up after we’re done.

After doing the same for BoxY a call to lua_pop telling it to remove the top 2 elements from the stack cleans up after everything we’ve done, and we can rest assured that we’re being good stack using citizens.

Just like before, we need to add a call to readGlobals to our main function. Since we want to get the values of our globals after the script has had a chance to modify them, we need to place the call after our call to luaL_dofile. Like so:

luaL_dofile(lua, argv[1]);
// Begin new code
readGlobals(lua);
// End new code
lua_close(lua);

Accompanying Lua script to test our changes out:

print("X: ", BoxX, " Y: ", BoxY)
BoxX = 1.5
BoxY = 1.0

The output should look something like:

X:      0        Y:     0
x = 1.500  y = 1.000

Excellent. Now we can share data with our Lua scripts. But that still keeps us rather limited in what we can do in Lua. So, our next step will be to expose native code, so we can make use of highly optimized routines, or just already implemented functionality of the game engine.

Sharing Functionality

Now we have the ability to update our Box’s position. Since we want to have the logic for moving our box each frame implemented in Lua, we might want to start working on implementing a movement algorithm. So lets implement a very basic movement scheme which will bounce the box around the screen. Very simple. But first, we need a random velocity for our box. We can do this very simply in Lua, but for lack of a better example, I’m going to use this as a reason for exposing native code to Lua.

First, we’re going to want a function that can provide us with the x and y components to a randomly generated velocity. I’d also like to allow the Lua script to specify what the magnitude of that velocity should be, that way changing the Box’s speed doesn’t require a recompile.

In order to do this, we first need to define a native function that Lua can call. Since Lua is going to be calling this function directly, it has to match Lua’s C function prototype. This means the function has to return an int, and must take a single parameter of type lua_State*. The lua_State pointer is a pointer to the Lua VM that the function is being called from, and the return value of the function is the number of values the function returned on the stack.

Here’s a function that would generate a random 2D velocity (x & y components) that can be called directly from Lua:

int genRandomVelocity(lua_State *l) {
   float speed = luaL_checknumber(l, 1);
   srand(time(NULL));
   float x = (float)rand()/(float)RAND_MAX;
   float y = (float)rand()/(float)RAND_MAX;
   float len = sqrt(x*x + y*y);
   x = (x/len) * speed;
   y = (y/len) * speed;
   lua_pushnumber(l, x);
   lua_pushnumber(l, y);
   return 2;
}

genRandomVelocity starts out by checking to make sure the first argument (stack item #1) is a number. If it is, the number is copied to our local variable speed. Once it has the value for speed, it generates a random 2D vector, normalizes it, and then scales it to the Lua supplied magnitude (speed).

Once the vector is calculated it pushes the component values onto the stack using lua_pushnumber. The values should be pushed in the order that you want the user to retrieve them. Finally, the function returns 2, telling Lua that it has 2 elements on the stack that it is returning.

So now that we have a function to generate our velocity we can expose it to Lua from inside our initializeGlobals function with a quick call to lua_register like so:

void initializeGlobals(lua_State *l) {
   lua_pushnumber(l, box_x);
   lua_setglobal(l, "BoxX");
   lua_pushnumber(l, box_y);
   lua_setglobal(l, "BoxY");
   // Begin New Code
   lua_register(l, "randvelocity", &genRandomVelocity); // Tell Lua about our function
   // End New Code
}

Which allow us to use the Lua function randvelocity inside our script:

-- Generate a new random velocity
x, y = randvelocity(2.0)
print("vx: ", x, " vy: ", y)

Which should generate an output like this:

vx:     0.00092774751828983      vy:    0.012465523555875

Which will naturally give us different values each time we run it.

Ok, so now our Lua script can utilize data, and functionality, implemented in our native code. But, unless we plan on storing every piece of Lua functionality in its own file, we need a way to call Lua functions from native code.

As we’ve seen, by using luaL_dofile our script will be read, and evaluated, by the Lua VM. So, we’ve been relying on the evaluation in order to execute our previous test code. What we can do now, is modify our script by adding a function called ‘update’, and moving our Box position code into it. By leaving our velocity generation code outside of the function we can still take advantage of the evaluation to initialize our Box’s velocity.

-- Generate a new random velocity
x, y = randvelocity(0.0125)
print("vx: ", x, " vy: ", y)
 
function update ()
   print("X: ", BoxX, " Y: ", BoxY)
   BoxX = 1.5
   BoxY = 1.0
end

Now, if we run the same version of our native code that we had prior to making this change, all the output we should see is something like:

vx: 	0.011539350263774	 vy: 	0.0048055569641292
x = 1.000  y = 0.000

Which is great, because that means it is still running the code that we’re using for our velocity when it loads the Lua file, and not executing our update function at all. So, whenever we want to actually call that update function, we need to add some native code which, for now, can be added in right before our call to readGlobals:

luaL_dofile(lua, argc[1]);
// New code...
lua_getglobal(lua, "update"); // Push the 'update' function onto the stack
if (lua_pcall(lua, 0, 0, 0) != 0) // Call the function that's on the stack
   printf("An error has occured running 'update': %s\n", lua_tostring(lua, -1));
// End new code
readGlobals(lua);

Calling a Lua function from C follows a very similar pattern to getting values from our globals. First we need to tell Lua that we want the global “update” to be pushed on the stack. It doesn’t matter that ‘update’ is a function, Lua will treat it just like it treats any other global value. So our call to lua_getglobal will push update onto the stack for us, so we can use it however we want. In order to execute it though, we need to call lua_pcall (or one of the other lua_call-like functions) which will execute the function currently on the stack, and return 0 if it executed successfully. lua_pcall will also pop the function off of the stack, so we don’t have to worry about cleaning it up.

If the call to lua_pcall fails, Lua will push the error string onto stack index -1. Which is why I’ve printed out that stack element, as a string, if an error occurs. The strings generated will be what you would expect a Lua interpreter to tell you if you have syntax errors, etc.

Now that we can call our Lua function from native code, we can move towards simulating an actual game loop. By looping over lua_getglobal, lua_pcall, and readGlobals we can use the update function like it would be used on a per-frame position update.

function update ()
   BoxX = BoxX + x
   BoxY = BoxY + y
end
   for (int frame=0; frame<10; ++frame) {
      lua_getglobal(lua, "update");
      if (lua_pcall(lua, 0, 0, 0) != 0)
         printf("An error has occured running 'update': %s\n", lua_tostring(lua, -1));
      readGlobals(lua);
   }

Giving us an output like:

vx: 	0.010487142950296	 vy: 	0.0068021933548152
x = 1.000  y = 0.000
x = 1.010  y = 0.007
x = 1.021  y = 0.014
x = 1.031  y = 0.020
x = 1.042  y = 0.027
x = 1.052  y = 0.034
x = 1.063  y = 0.041
x = 1.073  y = 0.048
x = 1.084  y = 0.054
x = 1.094  y = 0.061
x = 1.105  y = 0.068

If we actually had a render loop, we’d see our box move across the screen in a random direction. But it would still leave the view, and we’d never see it again. So if we go back to our initializeGlobals we can put a couple more globals in to expose our viewport width and height. Lets assume we’re using an orthogonal projection with a view of x=[-1.0, 1.0], and y=[-1.5, 1.5].

lua_setglobal(l, "BoxY");
// Begin New Code
lua_pushnumber(l, -1.5f);
lua_setglobal(l, "MIN_HEIGHT");
lua_pushnumber(l, 1.5f);
lua_setglobal(l, "MAX_HEIGHT");
lua_pushnumber(l, -1.0f);
lua_setglobal(l, "MIN_WIDTH");
lua_pushnumber(l, 1.0f);
lua_setglobal(l, "MAX_WIDTH");
// End New Code
lua_register(l, "randvelocity", &genRandomVelocity);

This probably isn’t the most elegant solution, but it will work for this example, and we can now implement our update function like so:

-- x and y are defined outside the function as the x and y components of
-- our velocity vector.
function update ()
   nx = BoxX + x
   ny = BoxY + y
   if nx < MIN_WIDTH or nx > MAX_WIDTH then
      nx = nx - x * 2.0
      x = x * -1.0
   end
   if ny < MIN_HEIGHT or ny > MAX_HEIGHT then
      ny = ny - y * 2.0
      y = y * -1.0
   end
   BoxX = nx
   BoxY = ny
end

Running our code now, and modifying our velocity magnitude to 0.5, should give us an output like this:

vx:     0.47514313459396         vy:    0.15568877756596
x = 1.000  y = 0.000
x = 0.525  y = 0.156
x = 0.050  y = 0.311
x = -0.425  y = 0.467
x = -0.901  y = 0.623
x = -0.425  y = 0.778
x = 0.050  y = 0.934
x = 0.525  y = 1.090
x = 1.000  y = 1.246
x = 0.525  y = 1.401
x = 0.050  y = 1.246

A nice, and bouncy, moving quad. Updated entirely from our Lua script.

The next step..

Now we have some basic Lua support capabilities that can help us to move some of our game logic into script. This is great, if all we wanted to do was expose some globals, and share some functions. So, our next challenge, is to generalize our code so we don’t have to keep using the boilerplate Lua C API to add globals, and expose functions.

Code for this post.

Leave a Comment :, more...