New Lua user here...with lots of questions :)

Started by
15 comments, last by DrEvil 18 years, 10 months ago
I posted this over on www.lua.org, but their site doesn't seem too....active as of late. So I'm going to cross-site post in hopes of getting a response: I've been working on developing a game and I've been trying to integrate Lua into the project and have gotten overwhelmed in the design phase (not good, I know, but scripting is 100% new to me). I've looked around the net and couldn't really find anything that helped clear the confusion, so I figured I'd try here. Ok, basically I'm trying to use scripting to control as much as possible in my game to make it completely customizable....initialisation, controls, game states, AI (especailly the AI), everything. I know this is probably my problem, too large of a scope, but that's one of the things I'm trying to determine. Just to give you an idea of the data flow I've envisioned: the game will start up and use an initialisation script to setup the window and other global parameters. It will enter it's intro state (the default first state). The initialisation script will contain a variable with the name of a script file that will be used by the intro state (it will have a variable with the name of a lua script for each of the possible game states, so that each game state is controlled by a separate .lua file) During the play state, for example, the script play.lua could say: if(key_down(p)) changeState(pauseState) So when each game state starts up it grabs the name of it's script file from the initialisation script. Now the tricky part, each Agent in my game will be controlled via a script as well, when I create a RobotAgent, for example, I'll pass the name of the .lua file that specifies how that Robot should behave...and here I come to my actual questions. 1) From what I've described, there's going to be a ton of scripts running at one time...if it is even feasible to run multiple lua scripts at once, is this an efficient design approach to take? 2) I've read that you should only need/want one lua_State* variable to control your scripts...if I do this, is it possible to run multiple scripts from the same lua_State* or do I need to read up on coroutines? One benefit I can see from one lua_State is I'd only need to register with luabind once, but that leads into a whole new set of questions I have involving luabind...I'll hold off on those for now Smile 3) Say I create a RobotAgent and need to have several instances of this running around, each one is controlled by a script in Robot.lua. I'd need separate lua_States for each instance to guarantee that the different robots didn't corrupt the global variables in the script for other robots...but this would seem to contradict what I've read for (2)? If it isn't completely obvious yet, I feel a bit like a deer in the headlights of a big 18-wheeler right now Smile I'd greatly appreciate any help or words of advice anyone could offer. Also, if you know of any tutorials/articles on Lua out there that would address these issues, please post it here. Thank you guys so much!!!
Advertisement
Be careful you don't try to let lua do everything. Your performance will suffer greatly if you do this.

I'm using game monkey script myself, but the same ideas apply to most scripting languages. GM scripts has these fake threads(co-routines in lua) which allow any number of scripts to run together, and allows the script to handle yielding, blocking, sleeping, etc.

I use the scripting language in my FPS bot. It allows pretty much complete control over the bots, but still keeps a majority of the functionality in the C++ side. I take advantage of the threading support in GM script to give each bot their own 'thread', that can control the bot. For a full game each game object could in theory be running its own thread of logic. GM script comes with several console fully script powered games that do this.

Ideally for performance reasons your script execution would be limited to events, so if no events are firing, no script is being executed. It wouldn't be a good idea to execute scripts during the game loop(every frame).
1. It's probably not all that efficient, no. The most efficient use of scripting languages is when you can use them as event-based callback handlers. Slightly slower than that might be to write the main program in your scripting language and delegate performance critical code to C/C++ functions. Running multiple scripts at once is likely to be slightly slower still due to all the context switching, and is probably more complicated than the alternative anyway.

2. As implied above, generally it's better not to run multiple scripts at once. There's nothing inherently wrong with doing so, but it creates as many problems as it solves and most people are not very good with concurrent programming at the best of times, never mind in an unfamiliar language that is interacting with another language.

You can run your scripts totally independently using separate lua_states, but then you have to watch out for the shared access to your C++ variables. (If there's no sharing though, there's no problem.) Or you can use coroutines to simulate threads but you still need to consider the implications for shared data, and also how to schedule resuming each coroutine as needed.

3. Personally I would consider reworking the scripts to be a set of functions that are called at given times. But if you really want long-running looping structures, then put the script into a function and create a coroutine out of each one. There's not much point having separate lua_states unless they share no common variables or functions, which sounds unlikely. It's not a question of avoiding corrupting data, it's a question of whether they need to access shared data (eg. regarding their environment).
I've been looking into Lua for the same reasons as you lately. This is what I can tell from my limited experience with Lua:

I can't say much about having many lua_States, except that interaction beetween them would be difficult. I'd opt for a single lua_State for everything.

You can change the global table for functions (I can't remember the function to doit), so you can easily protect your globals, without having many lua_States.

Wether you should use coroutines or not depends on how your scripts are run. However you could decide on entity basis:

--- RoboAgent.lualocal f1=function (state)  --- Do robo1 stuff hereendlocal f2=function (state)  --- Do some initialization  while(true)    --- Do robo2 stuff    state=coroutine.yield()  --- Yield coroutine and on resume, get state  endendlocal co=coroutine.create(f2)local f3=function (state)  coroutine.resume(co,state)endreturn f1,f3--- Main.luaroboscript=loadfile("RoboAgent.lua")robo1,robo2=roboscript() --- Main loopwhile(true)  robo1()  robo2()end

I haven't tested that, so there might be some bugs, but the idea should work.
That AP was me. I could have sworn that I was logged in.
Also Lua is pretty fast, so there shouldn't be anything wrong performance wise to make the control code, main game loop and event handling in Lua, just as long as the rendering stuff and physics simulations and other CPU expensive stuff is not done in Lua.
I've basically implemented (I think) what you are trying to do...

I would rein in your ideas slightly though.

For scripted objects, I define global tables objectList and objectType.

For each new type of scripted object, I add it to the objectType table with the object's type-name as the key, and for each instance I add it to the objectList table with a unique Id as the key:

e.g. largeTank object type would go into objectType["largeTank"], and an instance of it would go into objectList[0]. objectType entries are tables containing a function create(), update(), handleMotion() and handleOrders(), as
well as any other objectType-specific data I wish to use.

When I wish to create an instance of an object type, I simply call objectType["largeTank"].create(-parameters-).

objectList entries are tables with entries pointing to the associated objectType field as well as any other data I need. When I call createObject C++-side, It automatically creates the new objectList entry and adds its Id to a global list.

Every update-frame: Call every objectType.update() function, objectType.handleMotion() function, and if we have orders pending, objectType.handleOrders() function.

The key is to create a library of C++ support functions to reduce the workload on your scripts.
-Scoot
I was worried about the performance/customizablility tradeoff. Outside of using the script to dynamically setup a level, game parameters, I was looking at a structure like:
In each frame{ for_each_game_agent {  agent.update() }}

Where Agent::update() would call a lua script for the agent. This script would look something like this:
function update()  evaluateAIGoal()    if(keyDown(key_left))    moveAgent(left)  if(enemyInSight())    shootEnemy();end

And the function calls within the script would be member functions of various classes (I'm planning on using luabind). Sorry for all the pseudo code, but I'm still planning at a high level and I'm trying to get guidance/advice without asking y'all to hold my hand :)
Ok... given that sort of layout, you're not actually running several scripts at once - you execute one, then the next, then the next, etc. This makes things a lot easier, providing you don't want to start doing long-running tasks in any of those scripts. Performance there should be perfectly adequate providing that functions such as 'evaluateAIGoal' are largely implemented in your compiled language.
Kylotan,
I'm not sure I follow the "execute one, then the next, then the next, etc." logic. I may be missing something in the lua doc, but I don't see anythign to stop a script besides the coroutine yield.

The biggest problems I see with calling lua_dofile multiple times is
a) I'd have to have unique variable names across all of my .lua files. I could put them all in tables, but then the table names would still need to be unique.
b) More importantly, I wouldn't be able to use something like Robo.lua to control two unique instances of a Robot.

This topic is closed to new replies.

Advertisement