Choosing a scripting language

Add comment!

January 20th, 2010

For the last week or two I've been working on choosing a scripting language for Overgrowth. I haven't worked with scripting before, so I hooked up Lua, V8 and AngelScript in order to give them each a fair trial and see which is most appropriate for us.

What is a scripting language?

In general, scripting languages are programming languages that are used to control other applications. In games, their use is more specific: they are embedded into the game in order to run external "scripts" (pieces of code that can be modified without recompiling the entire game). This is useful in several ways. First, it makes gameplay programmers more efficient by reducing the amount of time they spend waiting for their code to compile. Second, it helps enforce the division between the game engine and the game itself, encouraging modular and reusable code. Finally, it allows players to write their own scripts, resulting in much more complex and interesting mods.

I've avoided scripting in the past because native C++ code runs orders of magnitudes faster than interpreted languages. However, this is not really that important: high-level game logic is almost never a performance bottleneck. I've seen how much live texture and shader updating speeds up my graphics work, so the idea of live code updating is very appealing.

What do we need from a scripting language?

There are three things I need from a scripting language. First, it has to be easy to embed in Overgrowth. Second, it has to be similar to C++. Third, it has to support user-defined types and operator overloading.

1. Easy to embed

In order for a scripting language to be able to do anything, it needs to be connected to the game engine. For example, a gun class in the game engine might need to call a 'shoot' function from a script, and the script would then need to call a 'raycast' check from the engine to find out if it hit anything. The easier it is to share functions and variables between the game engine and the scripting language, the less development time it will take to hook up.

2. Similar to C++

Many of you probably disagree with this point, but hear me out before writing irate comments! By using a scripting language that's similar to C++, I can prototype performance-critical functions, and then smoothly copy them into the game engine code when they are done. Constantly translating code between C++ and Scheme, for example, would be really annoying and error-prone.

3. User-defined types and operator overloading

This is also controversial, but I find that operator overloading is essential for writing maintainable linear algebra code. Operator overloading lets us add, subtract and multiply vectors and matrices as easily as we add, subtract and multiply integers. For example, let's say I want to find how far apart two players are on the screen (in order to make sure their names don't overlap). I won't get into exactly how this works, but using operator overloading this would look like this:

float distance = length(
    (model_view_matrix * player_one_position).xy() -
    (model_view_matrix * player_two_position).xy());

Without operator overloading, we have to replace all of those basic math operators with member functions like Multiply() or Subtract(), making it look more like this:

float distance = length(
    (model_view_matrix.Mult(player_one_position)).xy().Sub(
    (model_view_matrix.Mult(player_two_position))).xy());

I'm sure there are many great programmers out there who can read and debug code like that (the entire Scheme programming language is based on this principle, for instance) but I find it difficult!

With these requirements in mind, let's evaluate our three candidates: Lua, V8, and AngelScript.

Lua

Lua is a very popular game scripting language, used in everything from Aquaria to World of Warcraft. Because it is so popular, it has specialized libraries like Luabind which make it easy to embed in C++ programs. It does support operator overloading, so it should be easily usable for 3D vector math. However, it would be very difficult to port Lua code to C++ and back. First, Lua is a dynamically-typed prototype-based language, while C++ is a statically-typed class-based language. Second, Lua has its own idiosyncratic syntax -- here is an example:

-- Print numbers counting from 1 to "to_what"
function count(to_what)
    for the_count = 1, to_what do
        print(the_count)
    end
end

V8 JavaScript

While Lua is a popular game scripting language, JavaScript is the most popular scripting language for everything else. We already use JavaScript for our WebKit UI layer, but is it good for game logic as well?

I found that it is quite difficult to expose C++ functions to the V8 JavaScript engine -- there is no established Luabind equivalent, so I had to create a much more low-level wrapper than I did for Lua. This is not so hard for simple functions and variables, but it would be very difficult to expose complicated classes and member functions. Also, while JavaScript syntax is superficially similar to C++, it does not support operator overloading, and is structurally much more similar to Lua (JavaScript is a dynamically-typed prototype-based language).

AngelScript

AngelScript is virtually unknown compared to JavaScript and Lua -- I only heard about it from our friends at Frictional Games. It has native support for C++ functions and types, so it is very easy to hook up to the game engine. The syntax and structure of the language is very similar to C++, except with garbage collection and a few simplifications. It also supports operator overloading, and even comes with example code for setting up a 3D vector type.

Given the three things I was looking for in a scripting language, AngelScript seems ideal. Now the game engine code, gameplay scripting code and even GLSL shader code all use the same syntax! For example, the following code would run in the engine, in a script, or in a GLSL shader:

if(target_acquired){
    point_of_interest = target_position;
} else {
    point_of_interest = GetRandomNearbyPoint(head_position);
}
vec3 head_direction =
     normalize(point_of_interest - head_position);

If this code doesn't make sense to you, and you're interested in how it works, you can read my Linear Algebra posts (part 1 and part 2).

Uses for scripting in Overgrowth

There are a lot of possible uses for scripting in Overgrowth -- right now I'm mostly interested in using it for developing the movement, AI and fighting systems more rapidly, but it can be used for many other applications as well. For example, I'm thinking of eventually using scripts to handle spawning detail objects like grass and small rocks, and to direct dynamic weather effects. Can you think of any other ways we could use scripting?