Picture this scenario. You are developing a game engine, but get to an all-important question: “which scripting language do I use?”. The problem is, there are tons of scripting languages out there. Which one do you choose?
Unfortunately, picking a scripting language is not always easy. A lot of the time its a trade-off between performance, support, and something you like. There’s not much sense in picking the fastest scripting language in the world if you hate it after all.
For me, I don’t particularly care about the speed of a language (this has been explored before), so long as it’s reasonably fast. After all, if I’m going to implement something CPU intensive, I’m probably going to just end up writing it in C. I’m also open to anything, so long as it meets the following requirements:
- Reasonably easy to embed
- Uses an encapsulated state instead of globals
- Has a capable binding API
- Has some sort of OOP system with objects bound with functions
- Is used by a community of people
Since I had time to spare, I decided to evaluate a few popular scripting language implementations which have been described as being embeddable and fit the above criteria. Basically I wanted to:
- Bind an object to the API
- Pass an existing instance of an object to the API
- Create an object in the scripting language
- Call methods and get/set properties on the object
- Handle errors sanely (i.e. no stack corruption)
In addition I didn’t want to rely on any third-party wrappers – instead I went straight to the C API, since otherwise I would have been evaluating the scripting engine + the “helpful” binding library, rather than the scripting engine itself.
I ended up making a test application which binded a test object to each target language and ran a sample script in order to get a feel for how everything worked. The code is on github, so feel free to check it out if you are wondering what its like to bind a C++ object to lua, mruby, squirrel and angelscript.
I wrote a few notes in the code, but I felt the need to express my thoughts on each language based on my experience in working with both the API and language myself. So behold! My evaluation of each language is as follows…
Lua is a powerful, fast, lightweight, embeddable scripting language – or so it is claimed. It seems to be the number 1 choice for game developers, as this wikipedia article shows.
Scratching the surface, it appears to be a simple functional programming language. However it features some very powerful constructs centred around the concept of a “metatable” which amongst other things can be used to create class-based object systems. The API is well documented, and its fairly easy to find information on how to do just about anything in Lua since its commonly embedded in apps and games.
An often-heard complaint about lua is how it indexes arrays from 1, though in practice I don’t really find this to be much of an annoyance especially considering luas support for iterators. Another issue is how it isn’t very C-like in general, but IMO this isn’t too big of an issue since the syntax is fairly trivial to learn and get used to anyway.
The binding API in lua is a bit confusing as it uses the stack to set fields on objects, which almost feels like reading some assembler code. Once you realize how the stack works though, it’s not too bad.
Binding objects themselves is an open-ended problem, with many solutions. At a bare minimum you need to create a metatable object for the class, and for each instance of the class you need a userdata object bound to the metatable. You can just use a userdata for object instances and implement “__index” and to return methods,
but things start to get tricky when you need to call functions as if you just return a C function from “__index” lua won’t provide any reference to the userdata, so you need to pass in whats known as an “upvalue” when returning the function.
UPDATE: turns out it does provide a reference to the userdata, you just need to use the “:” instead of “.” when calling the function. Basic Lua 101.
One gotcha with the C API to look out for is when calling methods within methods. If the method you are calling returns an error, lua will pop the C stack all the way down to your first lua_call. This will ignore destructors for any stack variables you still have in scope in-between. To mitigate this, make sure to use lua_pcall instead.
For derived classes you can simply make a new metatable and set its “__metatable” to the parent class metatable, ad infinitum.
Apart from that binding objects to lua is reasonably simple, and if you are having problems there are a ton of binding libraries out there to help. A fairly strong contender.
Squirrel is a high level imperative, object-oriented programming language, designed to be a light-weight scripting language that fits in the size, memory bandwidth, and real-time requirements of applications like video games – or so the squirrel website claims.
In reality, I like to think of it as the result of what happens when someone takes a lot of the bad things about lua and fixes them to create a rather neat C-like language.
My only big problem with squirrel from a language perspective is its odd use of the “<-” operator to set object slots, though this can be “fixed” by simply changing the bytecode used by the “=” operator.
Similar to lua you use a stack to bind functions and classes which can be confusing. Thankfully though as with lua there are comprehensive API docs, but with one fatal flaw: I couldn’t find any good example for how to create a class bound to a C++ object, so once again I had to guess. I could excuse this a bit in lua since it’s more ambiguous, but in squirrel classes are a first level construct, making it strange how it isn’t really better explained in the documentation.
In general while I found the API documentation to be comprehensive, I didn’t feel it explained how to do things very well, and combined with the relative lack of resources online it made things a little harder to figure out.
If you are fed up with luas syntax and lack of certain basic constructs, be sure to give squirrel a try.
mruby is a light-weight, embeddable implementation of the Ruby programming language which has been in development for the past couple of years, so its fairly new.
Ruby itself is a very capable dynamic, object-oriented language which to me at least feels like a cross between perl and python. Ruby also has a proper inbuilt class system which IMO sets it on a higher level than lua. Not only that, it even supports composition through the use of modules.
Unfortunately the binding api is a little under-documented. In fact at time of writing I couldn’t find any documentation other than blog posts. In order to write a binding I had to essentially guess what you had to do using a combination of code on github and deduction.
However upon reflection binding an actual object is rather simple, with some rather descriptive function names to define both the class and its methods. For object instances, you use userdata instances bound to a data type.
Similar to lua there is also a gotcha when calling methods within methods. If the method you are calling raises an exception, mruby will pop the C stack all the way down to your first mrb_funcall. This will ignore destructors for any stack variables you still have in scope in-between. To mitigate this you need to save the “jmp” variable before the call and restore it afterwards. If you don’t want the exception to propagate down the stack you also need to clear the “exc” variable. If mruby had mrb_funcall_safe, this hack could have been avoided.
Compared to lua & squirrel, ruby is in general a more extensive language. But mruby itself still feels like it’s not quite there yet, so if you are considering implementing ruby in your app you might want to consider embedding the reference implementation instead.
Angelscript is a real curiosity. In essence it answers the question: “What would happen if you made a scripting language out of C++?”.
What results is a strongly typed, inflexible language. When compiling, everything needs to be defined or declared up-front. There are no fancy metaprogramming constructs, nor any late binding of functions. From what I gathered, if you make your whole game in a set of AngelScript files, you need to compile everything together in a single module. It makes me wonder, why not just use a reloadable DLL written in C++?
Unlike C++ Angelscript prefers that you define all class and namespace methods within the class and namespace blocks, making it feel more like C#. This is a fairly minor issue, but if you start thinking “maybe I’d like it better if I just wrote everything in C++”, it does not bode well for the scripting language.
However out of all the scripting languages, I’d have to say AngelScript was by far the easiest to embed. You simply define your function prototype and BAM! It figures out how to call it without having to write your own binding function (using its own FFI system). You can also directly expose object fields too which is a nice touch.
If you want something more direct, Angelscript is a safe bet. If you want something more freeform, look towards the other scripting languages.
So which scripting language do you use? Ultimately there is no perfect solution, you just have to go with the one you feel is the best. Failing that, you can always make your own scripting language, which is not at all an unusual thing to do when making a game engine!