In this post we are going to be talking about initializing your lua environment and setting it up.
At some point inside of your interface lua file you are going to need to initialize your object and component management system. Thanks to lua this is actually pretty straight forward however it can sometimes be a bit confusing. What we will be discussing in a moment is in regards to a game engine which supports both C++ and lua components, or what I call "hybrid game objects." You may be making a game engine which supports ONLY lua components in which case you would do this slightly different.
The very first step is to make a table which will hold ALL of your game objects, or if you wish to separate your game objects into different spaces (or game spaces) then a table what holds all of your game spaces, which are tables which hold all of your game objects.
If you plan on using type reflection (or having meta tables for each of your C++ types inside of lua) then this is the time you want to do it. Register inside of lua all of the meta tables for your C++ types, while doing this I suggest you store them inside of a global table where you can later access them for extending their functionality (for example store them inside of a table called "_R", why _R? Because the global table can be accessed through _G and we use _R for "registered types". You can call it whatever you want though.)
Example code:
<missing picture>
The next thing we are going to want to do is bind any static or library functions to lua. These sort of functions are the ones which expose engine functionality to lua, things like graphics, physics, game state management, ect. Don't bind any C++ component functions however, these should all be static functions. How exactly you do this is up to you, a simple approach is putting all of your bind commands inside of a function and then calling it.
Right after initializing your lua environment the very first script you should call is responsible for initializing the lua side of your interface. Personally I call this file "interface.lua" however you can name it something like "init.lua" if you prefer. This file is responsible for laying down the foundation for everything inside of lua and creating any global tables that other scripts will rely upon.
One the first things you should do in this file is declare functions for loading other lua files. A lot of times just using "dofile(file.lua)" is not enough since you might want to abstract away file paths from the user or keep track of all files that have been loaded.
After that you are going to want to load any utility functions and debugging functions. Personally I declare functions for trace logging, printing lua tables, checking if something is a valid C++ object, running a string as a lua script, and any last minute sanity checks for global tables which should be initialized but might have been missed (somehow.)
Next you are going to want to load any important modules like an event hooking system, lua script hot-reloading, action lists, math libraries, ui management, type extensions, draw call wrappers, ect. Having a function which will recursively load all lua scripts in a folder is super useful for this since you can then load all of your libraries inside of a "libraries" folder and have them all loaded in one go.
Directly after our initialization stage of our interface where we are loading in all of our modules and utility functions we are going to want to load in all of our lua types which we will be using later on. These types could be things like components, dimensional vectors, color types, quaternions, whatever you want. As we talked about earlier with having all of your registered C++ types inside of a folder called _R we can also do the same with all of our lua types/classes.
I do this by having a function called GetMeta which takes the name of a type (lua or C++) and returns the corresponding meta table. Since all of our meta tables are stored inside of _R this becomes a simple task, in the case that the meta table doesn't exist then it creates a new meta table for that type and returns it. Inside of each lua type file I start it off with getting the meta table of the type i'm about to declare, and then adding on member functions which I want to be accessible from the type.
<missing picture>
Now that each type has a meta table accessible from the GetMeta function, you can instantiate new instances of that type with a few lines of code:
<missing picture>
If you want to get a bit fancy (which i suggest you do) then you should add a function (called something like _New) by default onto each new meta table you create which just returns an empty table with it's meta table set to that type. (return setmetatable({}, meta) Better yet, have helper functions which act as constructors for types like Color or Vectors.
Something that that Trent Reed mentioned which is a very useful tip is to declare the __call (that has two _'s) function inside of meta tables so that they may act as a constructor and return an instance of that type. For example if you had a Vec3 meta table and made it's __call function take three values which are then returned as a Vector 3 you could do something like this:
local pos = Vec3(1, 0, 0)
A future article will talk more about using and getting the most from meta types.
Most likely there will be some C++ component functions which you are going to want to access inside of lua. A perfect example of this may be your Transform component's "GetTranslation" or "GetScale" function which most likely is written inside of C++ but will often be accessed inside of lua. The most important part about this however is having a function delegate system inside of C++, otherwise this will be very tedious and time consuming to do. I will assume you have one.
Now for each C++ component function that you want to bind to lua you should do the following:
<missing picture>
Essentially what this is doing is it's adding a C++ function to be called inside of the meta table for the C++ class which the function is a member of. The way we generically do this is we have a generic function (GenericFunc) handle all lua to C++ function calls, this function however interprets a function delegate pointer (fn) which we attach onto the function using lua_pushlightuserdata and then having a 1 inside of the last argument of lua_pushcclosure which pushes the actual C++ function. If you're curious on what exactly those functions do then you should check out the lua reference manual.
Now that you've set up your lua interface and have the foundations for great things, you're going to want to be implementing some sort of lua component and game object management system. We are going to be covering this in a later blog post so stay tuned for that.