Game Scripting Languages

Many games use scripting languages for animation and game play logic. This has the advantage of quick prototyping, and better organization of code. Almost every non-trivial game engine uses some scripting language.

There are many scripting languages suited to such a task. Lua is perhaps the most popular game scripting language. Other choices include: AngelScript, GameMonkey, Io, Pawn, Squirrel, and Scheme. Sometimes heavy-weight languages are also used, like Python or Ruby. These languages are usually quite a bit harder to embed, and aren’t know for their speed.

In this post I will compare AngelScript, GameMonkey, Pawn, Lua and Squirrel for my comparisons. I’ll also take a quick look at TinyScheme, although not run it through all the comparisons since it is interpreted. Io looks very interesting, but unfortunately it is severely lacking in documentation and won’t be compared here.

Language Introductions

Pawn is the odd one in the group, so we’ll start there.

Pawn (formally called Small) is small and simple. It uses a C like syntax and has only one data type, the cell. A cell is usually an integer, but it can also be treated as a character, boolean, or floating point value. Pawn has no support for structures or classes, but structures can be faked using named array positions.

Pawn is the only language in this roundup that completely separates its compiler from its virtual machine. It also, by far, has the most static compile time checks. All variables must be declared, and all native functions must have forward declarations. This is nice, because it alleviates run time checking on native function parameters. And of course, it’s much quicker to find an error at compile time than at run time. I found the compiler to also have very thorough warnings, for example it even warns about inconsistent indentation. My only grievance with the compiler, is that it requires leading zeros for floating point values. For example, it won’t accept .5 as a constant, but instead requires 0.5.

Pawn has very through documentation, has enjoyed some widespread use, and has an almost inactive forum. Pawn is the quickest and lightest scripting language I’ve seen. I’m using it for low level scripting in my game, including character and object animations.

Lua has its own unique syntax that’s a bit reminiscent of Basic (i.e. no curly brackets). It’s also very fast and compiles into fairly small byte-code. It’s very dynamic, variables don’t need to be declared before use, and functions are first class values (meaning that the compiler and abstract machine are very tightly coupled, and functions can be stored in variables). Lua makes extensive use of tables (associative arrays), which are its only complex data type. Tables are able to mimic classes and objects, by using some entries to store functions (first class value, remember) and some entries to store data.

Lua has been used extensively in the industry, has an active online community, and a large base of open source modules. Lua has predated and influenced every other language in this roundup. I found that Lua’s reference API documentation seems a bit vague until you get the hang of it (but there are many examples that make up for it). Lua also has a book available, Programming in Lua, that covers both the language itself and embedding, in detail.

GameMonkey borrows some concepts from Lua, but uses a C like syntax. It makes heavy use of tables, and can fake objects using tables the same way as Lua. It has finite state machine support, variables don’t need to be declared, and functions are first class values.

GameMonkey’s speed and small byte-code size really surprised me (it’s lean and mean). However, its API reference documentation is quite lacking. The source code is commented with Doxygen statements so running Doxygen helps (I couldn’t find a distribution of the generated reference online). It comes with a remote script debugger. I only played around with debugging briefly, but it’s quite neat.

There doesn’t seem to be a lot of hype surrounding GameMonkey, but they do have an active forum. Several community members seem to contribute back to GameMonkey, community contributions include many bindings, an even better debugger, etc.

Squirrel is a high level dynamically typed object oriented language support classes and inheritance with C like syntax. It also borrows tables from Lua. It was very easy to compile (one makefile) and seems to have top notch documentation.

Although Squirrel is still young, it has already been used in some commercial applications and has an active online forum.

AngelScript is a statically typed language with a C++ like syntax and classes. AngelScript has the best native binding in the bunch. Usually, a function or class only needs to be registered with the AngelScript virtual machine to be usable by a script. All the other languages in this roundup require intermediate helper functions for binding. However, the scripts cannot be compiled unless each native function is first registered. This adds an extra step to anyone wanting to ship pre-compiled AngelScript byte-code.

AngelScript doesn’t support tables, and in fact they wouldn’t be terrible useful because of AngelScript’s static typing.

AngelScript has an active online forum, pretty good documentation, and seems to be updated often.

TinyScheme is also worth a mention. It is contained in one C source file. Unlike the other languages in this review, it is interpreted, and so is about an order or two of magnitude slower. However, if speed isn’t an issue and you just want to easily add Scheme to your project, I would highly recommend it.

I tried some other schemes, but always had trouble compiling. TinyScheme is easy to compile, has many options, and is easily hackable.

Version Tested and Licenses

Language Version License
AngelScript 2.16.0 zlib
GameMonkey 1.25 MIT
Lua 5.1.4 MIT
Pawn 3.3.4058 zlib
Squirrel 2.2.2 zlib
TinyScheme 1.39 BSD

Binding

Embedded scripting languages are useless without being able to call native C functions or C++ methods. AngelScript easily wins here, just tell the library about your function and it’s instantly available to the script. The other languages need some glue code.

Pawn passes its function parameters as an array of ints. Floats simply need a cast, but for strings you’ll need to call a couple special functions first.

GameMonkey, Lua, and Squirrel use a stack to pass parameters. Because they are dynamic languages, values can be popped off the stack as different types (e.g. int, float, char*). The idea is a bit tricky to grasp, but it works quite well once you get the hang of it. Each language defines several macros to make using the stack a bit less verbose.

Each library also has third party libraries available to make binding easier. For example, here is a list of binding libraries for Lua. Personally, I feel you’re better off using native bindings, maybe with some custom macros. I question the reasoning behind wanting to use a bloated template binding library to expose your whole engine to a scripting language automatically.

Concurrency

AngelScript, Lua, GameMonkey, and Squirrel all support some form of concurrency. This allows scripts to create threads that appear to run parallel in the virtual machine. This is useful for when a script needs to run a long algorithm without disrupting its other responsibilities. These threads don’t give a performance advantage, but rather a programming advantage.

GameMonkey has native support for blocking threads until another event happens. For example, if one thread needs to wait for a door to open, it can go to sleep until another thread throws a “door open” event. I don’t think this would be hard to add with the other languages, but it’s nice that GameMonkey already has it.

Speed

I did a few speed tests in each language. These tests in no way reflect actual real world scripts, but they should give a comparison of each language’s basic overhead. The Fibonacci test tests function call overhead, by having a script function calls itself recursively 1,402,817,464 times. The prime test implements repeated iteration and basic integer math. The native string tests has the script call a native (C or C++) function ten million times while passing a 12 character string argument. The native number tests calls a native function a billion times while passing a single numeric parameter. Lua only supports doubles, but the other languages are tested by passing an integer type variable.

script_time

Each test was first compiled to byte-code, so the test time includes loading byte-code from disk, but it does not include compiling times for any language. Pawn, Lua, and Squirrel come with stand-alone compilers. AngelScript and GameMonkey use byte-code, but I had to actually throw together programs to save compiled scripts (which wasn’t a huge deal in either case).

It’s worth mentioning that Lua, Pawn, and Squirrel have Just-In-Time (JIT) compilers available. I didn’t test any of them. Pawn comes with a pre-compiled assembly implementation, so I did test it.

Since scripting languages are often used for string processing, I also bench-marked a script passing variable length strings to native functions. Pawn represents strings in a special way inside of its virtual machine, and it can be costly to convert longer strings. The other languages don’t need any special conversion, and so have a relatively constant speed regardless of the string’s length.

script_native_strings

Byte-Code Size

It’s likely that a published game may distribute compiled byte-code instead of source code scripts. Each tested language produces fairly small byte-code, which makes most of these suitable for use as simple configuration scripts. In many cases, the size and speed may be less costly than traditional configuration files, like XML (which wasn’t designed for that purpose either).

script_size

Library Size

This is probably rarely an actual issue, but these are the virtual machine library sizes. In others words, if you want your program to run scripts, expect your executable to bloat by this amount.

script_lib_size

Syntax

I’m including the scripting code used in the benchmarks here. The tests should give you a basic feel for each languages’ syntax.

AngelScript, GameMonkey, Pawn, and Squirrel each use a C like syntax with brackets. Lua has its own syntax, which works quite well. Non-programmers may find Lua’s syntax easier.

With the excepting of scheme (.scm), I feel that the syntaxes are basically interchangeable. If you know C, should you should be able to learn 90% of the syntax for any of these languages in ten minutes.

Conclusion

Each language has its own strengths and weaknesses. If you need an embeddable scripting language, I hope this post gave you a bit of a head start.

I tried to be as fair as possible, but I’m sure I made mistakes. Any comments/suggestions/corrections are welcome.

24 Responses to “Game Scripting Languages”

  1. Joe Andresen Says:

    That is a very nice article. I use Lua for my stuff and have found the community to make the experience a lot better. A lot of my code/modules i use (lua pickle, lua interface, NumericLua, Event System) are from peoples public/free projects.

    You might also want to add Decoda as the debugger for Lua. It is literally like the Visual Studio debugger, and as long as your application has .pdb files it will work with embedded lua VMs. Here is a Video of how it works.

  2. benoit jacquier Says:

    Thanks for the article!
    However i did a small perf test with a fibonnaci function ( recursive ), and my result gives gamemonkey 2x faster than Lua
    Here with fibonnaci number: 43
    GameMonkey( 1.25 ): 151 sec
    Lua ( 5.1.4 ) : 317 sec

    Core 2 Duo / VS 2008

  3. Lewis Says:

    I recently discovered The Jim Interpreter. It’s a very small TCL implementation. If you like TCL (I do), it’s definitely worth checking out.

  4. xorgman Says:

    What did you use to create the charts and graph?

  5. Lewis Says:

    For this post I used Gnuplot. For some other projects I’ve used Ploticus. They both work quite well.

    The trick is to have everything scripted. I just hit go, all the tests are run, and the graphs are drawn.

  6. Alberto Says:

    Your numbers on squirrel are quite off. On windows and VC++ 2010 squirrel is like +/- 2Kb compared to Lua.
    Plus looking at a .lib file to determine the code footprint is non sense, you should look at linked executables expecially when comparing C++ and C libraries. And make sure you don’t link the CRT statically.
    Your preformance tests also seem rather off(big times). You should probably specify the tools/configurations you tested with.
    (BTW I’m Squirrel’s author :) )

  7. Lewis Says:

    Thanks for your feedback, Alberto.

    The libraries were compiled with GCC. I used the supplied Makefiles to compile both Lua and Squirrel. I will double check my comparisons between the two, and post back later.

    I don’t believe that looking a static library’s size is “nonsense.” It is the total compiled size of all the code. If you link it to an executable, and stripped out any uncalled functions, then obviously the size should be smaller. In any case, I’m displaying the information at face value, so readers should interpret it as they see fit.

    If you think my performance tests are off “big times,” then please, by all means, download the code I used, and run it (at least run “fib” and “prime”). Report back the run-times for Lua vs Squirrel, and the tools/configurations you tested with. I’d be happy to hear your results.

    I’m probably going to update this benchmark soon (it’s a year and a half old), and publish all my scripted benchmarking code. Tell you what, I’ll even compile some executables to compare library size with.

    I’m sorry if feel that this benchmark showed Squirrel in a bad light, but I am trying to be as unbiased as possible.

  8. Lewis Says:

    Just a follow up: I re-compiled Lua and Squirrel again, to double-check my previous results.

    The results from today are:
    Lua Library: 197 KB
    Lua Executable: 197 KB
    Squirrel Library: 397KB (or 462 KB including sqstdlib)
    Squirrel Executable: 377 KB

    I compiled both Lua and Squirrel with the included Makefiles. It appears that both used optimization level 2 (-O2). My compiler is MinGW GCC 4.5.0 and the tested versions are Lua 5.1.4 and Squirrel 2.2.2.

    I also reran my “fib” and “prime” benchmarks, and got results inline with my previous results.

    If I have some free-time later in the week, I’ll run some tests with Visual Studio. If I get different results, I’ll let you know.

  9. Pekuja Says:

    I found this blog post extremely interesting. I think it might be the only such benchmark around. Now, I was hoping to try and replicate the test with the latest versions, but you seem to have omitted the embedding C code from the package. Do you still have it?

  10. Pekuja Says:

    I replicated some of the tests using latest versions of AngelScript, GameMonkey and Squirrel, as well as ChaiScript. I didn’t do a compile to bytecode first though, but I think the effect should be mostly negligible. I’ve posted my results on pastie.org as plain text for now: http://pastie.org/1721408

    I actually got some pretty different results on some of the tests. AngelScript did a lot better in my tests. Squirrel also did better, and GameMonkey did a bit worse in some tests. ChaiScript was the slowest of the bunch overall, but did surprisingly well in comparison when passing large strings to native code.

  11. Alberto Says:

    Ok, I noticed that indeed MinGW creates executables that are 2 times the size compared to Visual Studio when using C++ but not when using C.
    So I played a bit with my makefiles and I brought the exe from 400Kb to 290Kb… (BTW static libs are still the same size)
    First of all I found that I was compiling with C++ exception handling enabled, that squirrel doesn’t use. Apparently hits performance and size very hard in GCC (Add -fno-exceptions in the GCC parameter list and TADA! good performance gain).
    Running a “strip sq.exe” (like lua) reduces size even more. I tried running ‘strip’ on my libraries crashes strip.exe(at least the version I have).

    So I apologize if I couldn’t believe your results, it seems my GCC makefiles are way too simple and by default GCC leaves symbols both in static libraries and executables and they have to be stripped explicitly(my bad).

    ciao
    Alberto

  12. Deryck Says:

    Interesting results – I was considering using AngelScript for a game engine, mainly due to the ease of adding native functions to the scripting engine, but judging from these tests, Pawn looks like a great alternative in terms of raw performance.

    Judging by Pekuja, though, it appears things have changed since when you posted this (2009?). Just wondering whether you could repeat the same tests with your test harness, or whether you could post your test harness up so we can test it ourselves under similar conditions?

  13. dotsquid Says:

    Hi. Thanks for the article.
    However I’d like to join Deryck’s request to repeat the tests.

  14. Lewis Says:

    I’m planning on repeating the tests soon, and I’ll release the full test harness when that happens.

    I’m staying pretty busy right now, though, so it may be a couple of months out, unfortunately.

  15. Jay Sherby » Blog Archive » Scripting Language Benchmarks Game Says:

    [...] Some other languages I considered but ultimately rejected include Tcl, Pawn, Scheme, and AngelScript.  Tcl and Scheme are a bit too old and stagnant for my tastes.  Pawn and AngelScript both have negligible use in the real world, making support a considerable challenge.  Also, Pawn and AngelScript are so similar to C and C++ respectively that it makes the languages pretty boring.  I will note for the record, though, that Pawn has shown excellent results in others’ benchmarks. [...]

  16. 2BAM Says:

    Hi, it would be nice to use Squirrel 3.0 (instead of/besides 2.2) in the new benchmark

    Regards

  17. update please Says:

    This article is 4 years old. I think a general update (new benchmarks etc.) would be nice.

  18. camlorn Says:

    Hi,
    This is just about the only scripting language comparison I can find for these languages. Unfortunately, I’m visually impaired, and can’t read your bar graphs. is there any chance of getting the numbers posted as well?
    I’m looking into writing a game, and would rather not do my own benchmarks to see what’s too slow for the kind of things I would probably be using it for (and, before I get shot down: http://www.audiogames.net, and I rest my case…).
    Thanks.

  19. Lewis Says:

    Hi Camlorn,

    There’s a lot of data in the charts. If I had to summarize, I would say that Pawn wins in all the micro benchmarks. Lua and GameMonkey are close behind, and then AngelScript and Squirrel not far behind them. They really aren’t that different though. The speeds are usually within a factor of two of each other. I hope that helps.

    I don’t know your programming background, but I’ll caution you anyway. These benchmarks are almost 4 years old. And performance is probably far from the most important thing in selecting a language anyway.

    Good luck.

  20. camlorn Says:

    Yes, well. The trick here is that I’d be considering them for realtime analysis of the environment. My programming background is sufficient enough to know that, but calling something slow means calling it once per second instead of 5 times per second or more. I appreciate the summary.
    At a minimum, anything works for game events. Few work for actually analyzing the level data to provide useful navigational hints.
    I’d also be using OpenAL, probably in the form of OpenALSoft, and the target demographic isn’t going to have nice gaming computers, so…but yes, I do understand that performance isn’t the end-all and am leaning towards Angelscript for the easy binding and the fact that it’s a lot like c++.

  21. Álison Fernandes Says:

    Hi,

    This was exactly the article I was looking for, thumbs up! I also would like to see the same being done with the latest versions, it will be nice to see the improvements done in the past 4 years!

    Cheers!

  22. Martin Says:

    I tested debug and release build in terms of speed on fibonacci and primes on Angelscript 2.28.0
    Debug Fibonacci = 273.030 seconds
    Debug Primes = 83.766 seconds
    Release Fibonacci = 128.632 seconds
    Release Primes = 48.497 seconds.

  23. SLC Says:

    I tested the speed of fibonacci and primes with a release build of Squirrel 3.1 Beta 1 with Sqrat:

    Fibonacci = m7:39s (459 seconds)
    Primes = m5:42s (342 seconds)

    I tested this on my application with just a few classes exposed to squirrel through Sqrat and all of the Squirrel STD librraies.
    A huge mistake that this article and everyone else made is to not specify the used hardware. So, I’ll specify the hardware in order to compare the application requirements.
    A very old low end AMD Athlon 64 X2 4050e @2100 MHz, 2 GB DDr2 @667 Mhz. The application used only one core since no multithreading was involved (%50 CPU usage).
    A few basic applications applications running in the background (such as an antivirus, music player, text editor etc.)
    I think I’ll try to test it again later on an even lower configuration (an old Intel Pentium 4 @2800 Mhz) to see how it performs there. My application is a GoG game style so it depends on speed for low end configurations.

    I hope the test results might come in handy for anyone who needs some infos :)

  24. Martin Says:

    My test was done with a AMD 8320 on single core, 24GB cheap DDR3 memory. I am tempted to test the Angelscript JIT compiler but haven’t had a need for it yet. of course all extra performance is good. had visual studio on and a few other things, really not that important compared to the hardware you are using.

Leave a Reply