Integrating with your codeAt this point, you see that it's not hard to put lua into your application. But what use is it at this point in time to you if you can't get it to do anything with your application. Think about it. At this point, we have a scripting system, but we have no real integration with your application. I can't use what I've built so far to actually do something, like getting an enemy AI to search for your player in game. What we are going to need to do now is expose functions, as well as methods to lua. So this is our next step. To do this, we're going to create a couple of simple functions that we can access via lua. So, what I'm building is a simple Manager pattern for NPC characters. It's a singleton object that allows the creation, management, and deletion of NPC's that we can have in a game. I want to restate that this manager I've created is purely for educational purposes only. It in no way reflects what I would use for an NPC manager in real life. Anyway, back to the project. I've created Binding01 as an example of what you can do when you integrate lua and your existing code base. Here's an overview of what's going on. I've created a singleton object called NPCManager. It essentially creates and manages NPCObjects. It uses an STL list to keep track of them (again, this is a simple example). Currently, NPCObjects only contains a 'name' object. This object is never directly exposed to lua. Any time that we want access to one of these objects, we do it through the name of the object. This is crude and in no way is indicative of what can be done, but it's a straightforward example. So, NPCManager creates and manages NPCObjects. NPCManager is a singleton, so it exists from the first time it is used until the end of its lifecycle. So, the next question is, how do we expose the NPCManager's functionality to lua? We do this through a glue function. So, what does a glue function do? Essentially, it's a communication function between lua and your code. So why do we need this communication? It's all because lua doesn't have the same variable types as C/C++. Lua specificsLife would be really good if lua data types and C/C++ data types matched. But if that was the case, then we'd be ending up exposing the same kind of troubles that we have with C/C++ (Memory allocation, type checking issues … lots more, but you get the drift). To help with this, lua is what's considered a dynamically typed language. In essence, lua doesn't have any variable types. There's no int, float, char or similar variable declaration in lua. The weird thing is, even though we've just said that there aren't any types in lua, there actually are. Say what? Well, by dynamically typed, what we mean is that the variable doesn't have any type definition, but the value does. Say WHAT? OK, this can be a little difficult to deal with, unless you've done any COM programming, or have worked under the hood with Visual Basic. If you have had this experience, then this is just a review for you; otherwise, we're off to a fantasy land of variant data types. A Variant is essentially a 'catch all' data type. By definition, any variable created as a variant is of a variant data type … wait … yeah, that makes sense … kinda. Here, let's try this. Say I have a new typedef (in pseudocode): typedef struct _variant { int type; union { int Integer; double Float; char String[255]; } } Variant; What we have here is a new type called variant. Based on the value in type, we would use either the Integer, Float or String fields to get at data. Every variable that we create based on Variant is a variant, but what it contains is defined by its value. It's that simple. And, it can be very powerful, as you'll see shortly. Please note that lua doesn't use this kind of structure for it's internal data type. It's much more complex than this. This was purely an illustration. So, all that lua understands is this one variant data type. The data that it can hold in this variant type is: nil, number, string, function, userdata, and table. So from this, we can see that a variable in lua can be numerical, character, or a function. The other two, userdata and table, I'll explain in a later document. So, if we can put data into this variant in numerical or character, hopefully there is some way, on the C++ side, to be able to get that data out. And there is. There are several functions available to convert and manipulate this data. Some of them are: double lua_tonumber (lua_State *L, int index); const char* lua_tostring (lua_State *L, int index); size_t lua_strlen (lua_State *L, int index); lua_CFunction lua_tocfunction (lua_State *L, int index); void* lua_touserdata (lua_State *L, int index); There are more functions for accessing arguments passed through by lua. So what do they do? Well, as they read, they convert data from one format in lua to something in C++. But what's up with that index element. What about if we want to pass in a couple of arguments? I mean, that's completely possible. So how do we handle that? The answer is: the lua stack. |
|