Sign in to follow this  
acidleaf

Lua integration into a game

Recommended Posts

I'm programming a game in C++, and I've decided to integrate Lua into it to separate several parts of the game. Currently my ideas of implementing it is to create a ScriptManager singleton class to handle all scripts in the game. This singleton will maintain a single Lua state which will be shared across every game object that are exposed to Lua. Then any calls to scripts will refer to this ScriptManager class and call something like ScriptManager::DoScript("script.lua");

[font=arial,helvetica,sans-serif]How do you guys manage scripts in your game?[/font]

Share this post


Link to post
Share on other sites
You're going to catch some flak for using a singleton, even if the use is justified. Thread safety should be a concern of yours, if you are using multiple threads. Otherwise, all I have to say is it sounds fine, but be aware that you generally only have to luaL_dofile(---) once, and then use C++ functions that interface directly with your virtual machine (so instead of ScriptManager::DoScript("filename.lua") I would suggest ScriptManager::some_function(some_args), where some_function pushes the Lua function name and arguments onto the VM's stack then calls said function. Remember, when you're executing a .lua, it defaults to the global namespace, and you dont generally want to access your scripts that way, except to initialize them.

But, others might know better. This is just how I use lua, and I dont run into trouble with it.

Share this post


Link to post
Share on other sites
So assuming I use the singleton implementation, I will have to set up a loop to call luaL_dofile for each of the scripts I will use in my game, and from there when I want to call a function in Lua, I will use the ScriptManager and manipulate the stack to call the Lua function?

Share this post


Link to post
Share on other sites
If I were in your shoes I would make a scripts folder, then load every script in the folder during runtime and put through luaL_dofile. This way the end user could add any number of extensions or addons as they wish. Create a special C++/Lua api, where you register certain C functions inside lua. These functions when called will act as hooks to common game tasks/functions. These scripts will allow any number of modifications to the game runtime. Once all scripts have been run execute the game with all changes in effect. The scripts should be made optional and not required to run a basic game. For example an optional script could add another level, or new kinds of weapons.

Share this post


Link to post
Share on other sites
I don't even go that far. I only run luaL_dofile on a .lua which calls a hooked C++ function that itself calls luaL_dofile on the argument passed. Thus, I never have to recompile the project when I add a new script, or when a script wants to import another script file (loading [i]every[/i] file in the scripts folder adds extra hoops for seperating one mod from another). In other words, the engine only looks for init.lua, and init.lua is just a series of "DoScript("file.lua")". By doing this, a mod manager can easily append or delete the varius DoScript("file.lua") lines and your mod is loaded painlessly.

Otherwise, I handle things exactly as you and kd7tck said.

Share this post


Link to post
Share on other sites
Thanks guys for the kind replies and suggestions. Another question, say I want to use Lua to configure my game GUIs. Do I expose the various GUI elements' member functions such as [font=courier new,courier,monospace]setPosition(x, y)[/font] to Lua, and then in the GUI's [font=courier new,courier,monospace]init[/font] method I run the Lua function that handles the positioning of these GUI controls? Or would it be a better approach to make the GUI's class (for example the MainMenu class) exposed to Lua and do everything in Lua instead?

Share this post


Link to post
Share on other sites
[size=3](Is there an edit post option here?)[/size]

Just to be clear, currently all of my game states are implemented as different classes (MainMenu, Options, etc.), and each of them has an init() method which is called when they are switched into the game.

Share this post


Link to post
Share on other sites
[quote name='Zouflain' timestamp='1347606895' post='4979978']
I don't even go that far. I only run luaL_dofile on a .lua which calls a hooked C++ function that itself calls luaL_dofile on the argument passed. Thus, I never have to recompile the project when I add a new script, or when a script wants to import another script file (loading [i]every[/i] file in the scripts folder adds extra hoops for seperating one mod from another). In other words, the engine only looks for init.lua, and init.lua is just a series of "DoScript("file.lua")". By doing this, a mod manager can easily append or delete the varius DoScript("file.lua") lines and your mod is loaded painlessly.

Otherwise, I handle things exactly as you and kd7tck said.
[/quote]

Why would I recompile after adding a script?

Simply program main() to search for all files in the scripts folder and load each one in. No need to pass every file name in through a lua file. This makes things easier for end users; if all an end user has to do is drop in a file then that is ideal.

Share this post


Link to post
Share on other sites
[quote name='acidleaf' timestamp='1347607716' post='4979981']
Thanks guys for the kind replies and suggestions. Another question, say I want to use Lua to configure my game GUIs. Do I expose the various GUI elements' member functions such as [font=courier new,courier,monospace]setPosition(x, y)[/font] to Lua, and then in the GUI's [font=courier new,courier,monospace]init[/font] method I run the Lua function that handles the positioning of these GUI controls? Or would it be a better approach to make the GUI's class (for example the MainMenu class) exposed to Lua and do everything in Lua instead?
[/quote]

That will depend on how much control you want to give to the end user. Do not do everything in lua, the game loop needs to be in C++ not lua. You could have a hook that generates a custom state dynamically and places it onto a global state machine.

Share this post


Link to post
Share on other sites
A somewhat different perspective here. I use lua in my RPG, for scripting things like quests, conversations, etc. Every time I run a lua script, I actually create a whole new lua instance (by calling luaL_newstate(), luaL_openlibs(), luaL_loadstring(), and lua_pcall()). I implemented things this way at first because it was easy, with the intention that if performance was an issue, I could always go back and optimize.

To my pleasant surprise, the performance has been excellent, and no need to chance anything. I should note that I call scripts relatively rarely (a script won't be called every time through the game loop), and my scripts tend to be quite small and self-contained.

Hope that helps,
Geoff

Share this post


Link to post
Share on other sites
I have yet a different perspective. I use Lua for everything except for the really performance critical stuff such as rendering, pathfinding and so forth. Even my main loop is in Lua. The C++ portion of Goblinson Crusoe exists only as a set of classes which can be instantiated from Lua to perform their tasks.

Share this post


Link to post
Share on other sites
[quote name='acidleaf' timestamp='1347607716' post='4979981']
Do I expose the various GUI elements' member functions such as setPosition(x, y) to Lua, and then in the GUI's init method I run the Lua function that handles the positioning of these GUI controls? Or would it be a better approach to make the GUI's class (for example the MainMenu class) exposed to Lua and do everything in Lua instead?
[/quote]

Your first approach requires that you tightly couple your GUI element to Lua, so that it can't be used without modification if you decide to move away from Lua in the future. Your second approach is better, but comes with its own set of problems. How much of the interface should you expose to Lua? By exposing the interface, how many other userdatums must be exposed in order to be passed as arguments?

I personally look at my scripts completely differently than my engine code, exposing specifically tailored and simplified interfaces to Lua, rather than the lower level ones.

Share this post


Link to post
Share on other sites
[quote name='kd7tck' timestamp='1347608878' post='4979987']
Why would I recompile after adding a script?[/quote]That was actually in response to acidleaf, who made it sound like he was hardcoding all of the script file refferences. Rather than do that, I merely hardcode init.lua, which has the ability to expand the library of lua functions however I or a modder sees fit.

[quote]Simply program main() to search for all files in the scripts folder and load each one in.[/quote]The problem I see there is with modding. If [i]every[/i] file is read indescriminately, you cannot then have a moda.lua and a modb.lua in the scripts folder unless you want the user to [i]always[/i] use [i]both[/i] mods he has installed there. By using a file to describe each file seperately, you can neatly store all of the lua files for a specific mod in a scripts/modname/ folder, with only the scripts/modname_init.lua called in the scripts/init.lua file. Disabling a mod, then, is as simple as removing the modname_init.lua refference in init.lua.

I suppose you could run every modname_init.lua using your blanket method, but enabling and disabling mods would still require you to remove and replace the init file for no net benefit over the alternative. As an end user, I hate having to move files around (and dont trust programs moving them for me), and so I tend to design code that leaves files in place. Deleting and replacing one line is easy to write a mod manager for and involves less file moving. I don't suppose either design is objectively "better" but I always side with less moving files. Edited by Zouflain

Share this post


Link to post
Share on other sites
Currently I'm using a single state shared across multiple Lua scripts. On the C++ side, the 'mod' definition will register all the .lua files necessary for the game objects, these files contains a factory to create an object (table) inside the lua state, so I don't need a class Enemy or Ally on the C++ side, I just need an Entity providing a minimum interface and which invokes the Enemy or Ally factories from Lua.

The Lua objects communicate using message passing, the interaction is coordinated on the C++ side, using uid's. And since the queues are also in Lua I have freedom to send from int's to complex data structures, if necessary.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this