Jump to content
  • Advertisement
Sign in to follow this  
NordCoder

Understanding Boss AI scripting (Lua with Luabind)

This topic is 2587 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

I am trying to grasp the concept of scripting while learning Lua with Luabind. I plan to make a simple space shooter that uses Lua/Luabind for scripting (events, in-game messages and AI) and SDL/C++ for the game engine to put my skills into action.

However, I have trouble understanding one thing. Suppose I want to script the AI behaviour of the bosses in the game. I have a basic Enemy/AI class in C++ which the Boss class derives from. Suppose that I initialize all my bosses with a path to a Lua script which defines their behaviour. In the Boss Update(...) method I, among other things, execute the script (which preferably uses coroutines or some kind of action-sequence manager to define movement, firing of weapons, spawning of minions etc.) which updates the game object (Boss). What I don't understand is how you can bind the particular instance of the Boss (the one that is calling the script) to Lua so that it acts on this and only this...

I've searched the internet far and wide without any luck and read the Luabind documentation, but I still can't seem to grasp this particular concept...Can someone please explain it to me?

Share this post


Link to post
Share on other sites
Advertisement
I don't think you can do that. This is a matter of design.
Design your engine so that the Lua script controls all bosses rather than just one.

Share this post


Link to post
Share on other sites
I'm interpreting your doubt as two separated questions

1. How do I instance a c++ object from a Lua script such that I can manipulate it as if I was doing it directly from c++.
2. How do I control the behavior of a c++ object from Lua without running Lua code each frame.

Is that it? Or I misunderstood you completely?

Share this post


Link to post
Share on other sites
I exposed my finite state machine and state interface to lua, luabind lets you do classes/inheritance with lua. I only have one type of AI at the moment but I just create a new type of finite state machine in lua using inheritance. I have a lua function "CreateMonsterStateMachine" or something, that returns a new state machine of that type - it takes the entity as a parameter so if there's anyhitng specific in needs to do, it can. My monster(s) are defined using xml, one of the options is the name of a lua function to use to create its finite state machine.

I can create monsters from a template (all use the same AI), or I can use a template and override certain parts (the state creation function). If I wanted a boss I could tell it to use a different state machine. The only difference between the state machines is the states it creates. States themselves are responsibly for asking the state machine to change state in ageneral way (change state "attack"), its up to the fsm to choose what state to use. Normal monster AI might choose just a basic "attack" state, the boss fsm might instead run a "attack_boss" state or some such.

Share this post


Link to post
Share on other sites
Long ago scripts were just small pieces of code which could be run on a home brew VM, they bound dynamic behaviors to heavy weight native objects. The VM would load and unload these scripts as needed for each object and also kept its execution context. Scripts then were just toy langues and ran very slowly. Now days scripting languages are very powerful, usually dynamic languages, jit compiled, etc.. They are probably more popular than heavy weight languages like C++ or Java. Some common ones like Python, Javascript, Ruby, Lua etc..

The way they are used in modern games is usually an entire subsystem within the game running in the scripting language. An entire framework built using the scripting language supporting stateful management of objects. The scripts can control things like AI behavior, creation of dynamic content, runtime configuration behavior, etc.. they arn't isolated segments of code anymore, they are more like complete subsystems.

Games like yours, in many cases you would have a manager running on the script side which setup and manage the level and creates the waves of enemies and their timing and movement patterns. The boss is nothing more than another object, with its own stateful execution. Since your using Luabind you can have the script object (ie an instance of a script defined class within the scripting language) hold a reference to the native C++ object on which it directly operates ie move and shoot. Some people create a much deeper binding so there is no distinction between the C++ object and the script object they are one in the same. The scripts just extends the implementation of the C++ object and calls into the C++ transparently.. Personally I think that hides too much and creates potential performance issues in hidden calls between C++/Script layers..

Good Luck!

-ddn

Share this post


Link to post
Share on other sites
@[color="#1c2837"]MatsK: I see, this makes sense together with what the others are saying
[color="#1c2837"]
@owl: Actually, I guess it is kind of a mixture of both (though mostly 2). I have a pretty good understanding of how to instantiate an object in Lua and manipulate it as a C++ object, but how do I link the object with C++ so my engine can update and draw it? Do I add it to some kind of EntityManager? As for 2, how do I do that? :) In my head, I imagined the Boss/Enemy/whatever object calling the script on each update frame but that might be a bit expensive performance wise, but I can't really see how else you would go about it.

@Nanoha: So if I understand you correctly, you basically moved your entire AI/monster part of the engine to the scripting side? C++ parses the xml (with a 3rd-party/custom xml parser I guess) and passes it on to a Lua script calling the appropriate function to create the correct FSM. These are then controlled entirely by scripting?

@ddn3: [quote[color="#1C2837"]Since your using Luabind you can have the script object (ie an instance of a script defined class within the scripting language) hold a reference to the native C++ object on which it directly operates ie move and shoot.][/quote] This is part of what I have had trouble figuring out. Thank you for the clear, thorough answer. Say I created a script called Level1.lua which is called each update frame (possibly luaL_dofile(...)) from C++. The script checks the time that has elapsed and spawns enemies/mines/bosses as needed. These objects could then either be controlled entirely on the scripting side or added to an EntityManager associated with the given level.

Am I getting any of this or am I misunderstanding you all? I think I am beginning to grasp the concept somewhat though laugh.gif

Share this post


Link to post
Share on other sites
Yep that's a good way to do it. There are many ways of course, one way is to build a level manager and level primitives and the script files (ie level01.lua) are nothing more than data files defining waves and types of enemies and their movement patterns.. It all works just fine for the small scale right, the main difference between these designs is scaling. If you had like 10 levels, you can duplicate functionality between level scripts but if you had say 100 levels, that would be alot of code to duplicate (each script file would re-implement basic level functionality which u would want to reuse and consolidate) and manage, so you'll switch to a more procedural method and data driven design.. Play around with it, the thing about dynamic scripting languages is once you get familiar with it, it takes about 1/4 of the effort to build complex things in the scripting language than equivalent C++ / C I've found. Also some people throw out good software design when they start coding in scripts for some reason, keep good software practices it will go along ways.

Good Luck!

-ddn


Share this post


Link to post
Share on other sites

@Nanoha: So if I understand you correctly, you basically moved your entire AI/monster part of the engine to the scripting side? C++ parses the xml (with a 3rd-party/custom xml parser I guess) and passes it on to a Lua script calling the appropriate function to create the correct FSM. These are then controlled entirely by scripting?


Correct, there are still things like path finding thats done in c++ but its accessed through lua. My entities are component based, one such component is an AI component, it has a fsm (which is just created in lua). It doesn't really care what type of fsm it is, so by changing that, I can change the behaviour of the AI.

Share this post


Link to post
Share on other sites
@ ddn3: Ok :) Thanks for the suggestions, I guess I'll just have to give it a try and code something then while trying to keep up good coding practices ;)

@Nanoha: One question though. I've read that when embedding a scripting system, you should let the engine (C++) side take care of the heavy stuff, i.e. drawing, collision detection, audio, physics and path finding while the script should do stuff like collision events, path finding selection, sound events and decision-making. Are you calling drawing functions from Lua (bound to the actual drawing functions in the engine) like the path finding functions? If so, does that still count as letting C++ take care of drawing or does that impose a performance penalty of some magnitude?

EDIT: @ddn3: Isn't it a bad idea to call the script with luaL_dofile each frame performance wise? Is there some other way?

Share this post


Link to post
Share on other sites

@Nanoha: One question though. I've read that when embedding a scripting system, you should let the engine (C++) side take care of the heavy stuff, i.e. drawing, collision detection, audio, physics and path finding while the script should do stuff like collision events, path finding selection, sound events and decision-making. Are you calling drawing functions from Lua (bound to the actual drawing functions in the engine) like the path finding functions? If so, does that still count as letting C++ take care of drawing or does that impose a performance penalty of some magnitude?


Its up to you how you want to do it really. Something like drawing/physics you will likely never need to change once you've got it right and you shouldn't need to directly access it. It should stay on the c++ side of things and get abstracted away. Same for drawing, You should be loading models, playing animations and whatever else in a slightly more general way than directly loading a model, calling whatever rendering you need. Its quite possible to do this stuff in lua if you expose it well enough but it wouldn't add anything and would lilely only slow things down.

As far as path finding goes, there's a graph of nodes and links between them, you want to go from A to B. All you really want to do is ask it for a path from A to B, and so thats all you might need to espose in lua - you don't need to expose all the internals of how its actually finding the path.

Another way to do it is to do most things in lua and only move parts to c++ if they are slow (as you say, the heavy stuff).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!