Hi all!
These last days I have been reading quite a lot about scripting engines or systems and why they should be included yes or yes in any worthy game engine. When I first heard about this, I have to admit that I had a narrow vision on its possibilities, maybe because I didn't fully understand the idea behind having an embedded scripting engine. Now that I understand it better, I want to share some of my (limited) knowledge with you.
Ok, so first of all: what is a script? According to Wikipedia, scripts are 'programs written for a special run-time environment that can interpret and automate the execution of tasks which could alternatively be executed one-by-one by a human operator'. Well, actually, the truth is that there is a fading boundary between a script and a 'traditional' program. Script languages are usually interpreted, which means that their instructions are executed on the fly by the interpreter, which usually is simply a virtual machine capable of understanding the high-level script instructions and of executing them on the target machine.
Traditionally, interpreted languages have not been considered a good choice for game programming, given the high performance requirements of this kind of software, which must run at a minimum of around 60 frames per second. However, as interpreted languages evolve and devices acquire more powerful processors, this is no longer completely this way.
Most game engines include scripting as part of their suite.
Well, it is true that the performance of a C or C++-written game will never (for the moment) equal the performance of the same game written in an interpreted language (or in other higher level languages like Java). But it is indeed possible to, let's say, outsource some functionalities to be implemented by these languages.
Let's go with a simple example. Let's suppose you're doing a side-scrolling game and you want that when the player reaches a certain position in the scenario, a particle system sparks off, e.g., an explosion goes off. Let's assume that you don't know anything about scripts, so you must hard-code this effect in the game logic, which would result in something like this:
void ParticleSystem::start(Position x, Position y) {
for (int i = 0; i < N_PARTICLES; i++) {
particles[i] -> start();
particles[i] -> setVelocity(...//random velocity for each particle);
... //more settings
}
}
void Particle::start() {
p = new Particle("particle.png");
}
void Level::update() {
//update of all the objects
if (player.position > X_EFFECT_POSITION) {
ParticleSystem::getInstance() -> start(player.position.x, player.position.y);
}
}
The code is not difficult to follow. I have to check, in each update of the game for a specific level, if the player has reached the specified position. [NOTE: I simplified the code here; I think that an improved version should use the
Observer pattern so that the player object itself notifies the particle system when it reaches the destination]. Then, the particle system initializes all the particles with a given image (particle.png) and sets a random velocity for each particle. So far so good.
However, when you run your game, you notice that setting the velocity to a random number does not provide a realistic explosion effect. Ok, you change the code, compile again, and run again. Ufff... the bitmap used for the particles is not that cool. Ok, let's create a new bitmap, compile again, and test it. And you can be repeating this process dozens of times until you end up happy with the result.
Now, let's assume that you implemented a scripting engine in your system. Let's see how the code in your game would look like (of course, this is no real code, just a simplification, as usual):
void ScriptingEngine::explosionEffect(Position x, Position y) {
initializeEngine(&sh); //sh stands for scripting handler
sh.pushParameters(x, y);
sh.executeFile("explosion.script");
}
void Level::update() {
//update of all the objects
if (player.position > X_EFFECT_POSITION) {
ScriptingEngine::getInstance() -> explosionEffect(player.position.x, player.position.y);
}
}
And now, we create a .script file that will do exactly what the older code did:
//explosion.script//
Begin Script
player.position.x = popParameter(1);
player.position.y = popParamenter(2);
image = loadParticleImage("particle.png");
... //code that creates all the particles with random velocity
End Script
Basically, you communicate the required arguments (pushParameters) from the host language (e.g. C++) to the scripting system. Then, the scripting engine executes the explosion effect implemented in the file.
Now, you compile and execute. Mmmm... you're not happy with the particle bitmap or with the velocity of the particles. Let's change all that. Need to recompile again every time? No! Because the effect is completely loaded in the script, you only have to change the .script file and execute the game again. The game engine has outsourced the explosion effect to a scripting system, and this provides higher flexibility and effectiveness. The explosion effect is totally decoupled from the game logic, so any change in the effect doesn't alter the game logic and vice versa.
This is a really powerful feature that pushes the development experience forward. The possibilities of this approach are unlimited: faster testing and debugging, easy change of characters behaviour, improved extensibility of the game (capability of adding new features effortless), and much more!. And even better: you could provide a terminal with the game so that you can write script commands while the game is running, and you would see the changes immediately at runtime, without the need for restarting it.
From a design perspective, the biggest challenge is to know which parts of the game are worth this outsourcing. If you read enough about it, you'll get the following answer: delegate to the scripting system any aspect of the game that has no significant performance impact. In my opinion, this can be re-formulated as follows: do not call the script engine once per frame. Anything that needs to be executed once per frame should be implemented in the host language (e.g. C++). But any effect or event that does not occur every frame is a great candidate to be delegated to the scripting engine. The more you export (with unnoticeable performance degradation), the higher the productivity boost you will experience.
Regarding the scripting language to choose, there are lots of them out there, and the decision is likely to depend on the host language. For C++, for example, it's common to see bindings with
Python and
Lua, given that they are lightweight (specially the latter) and are implemented in C. I'm currently looking at Lua because I know that it's being used currently in many commercial games and the API seems to be simple enough. I'll let you know about my progress soon.
Hope this post was interesting, or at least that you learned something new from it. Don't hesitate to ask or comment anything you want.
See you!