Unlike better graphics, scripting isn't one of those features that you focus on in your game engine, but it is an important component. These days, game engines are required to be extremely flexible which makes them equally complex. In order to meet these goals, it's crucial to have a data driven design focus where the engine behavior is controlled by external data. In a basic implementation, simple data structures such as Health, Speed, Object Placement and other game values are read from data files. We often need to do more than just specify the different colors of the enemies in a game, so this only meets half of our requirements. The fixed data format constrains what options are ultimately possible and it simply does not provided enough flexibility. We must also drive the game's logic and rules. This is accomplished using some form of scripting language. This article explains how you can easily create a simple scripting language for your game and provides some sample code that you can quickly integrate in to your own game engine. Traditionally games developed in-house scripting languages such as QuakeC, UnrealScript or similar resulting in a plethora of obscure game specific languages. Most people do not want to spend the resources to develop their own robust scripting language. A common option is to leverage existing languages like LUA, Python, Ruby or Java. Even when not, we are re-creating the wheel, using another embedded language isn't a perfect solution. It's still a hell of a lot of work to get them functioning and at the end of the day, there's a huge time investment to properly embed another language in to your game engine. And we all want to avoid as much of this work stuff as we can. 3rd party languages generally work great, especially some of the better supported ones but they do have their drawbacks.
Instead of embedding in other external scripting languages, it would be useful if we could use one we are already familiar with. C makes a wonderful base for representing a custom scripting language for your game. C will probably do every thing needed and you will be leveraging the power of a language with which you are already comfortable. It is much easier to distill down to elements that you need than to add a lot of additional functionality to something that does not do what you require. There are some obvious benefits
One of the biggest advantages is time and performance. It takes very little effort and time to learn and use. Chances are you are already coding in C/C++ and because it is all compiled code; the performance will be on par with whatever else you have written for your core engine. Using C is not without pitfalls either, the scripts can be very dangerous, and it would be difficult to create safe scripts that run in a sandbox. All this wonderful power means we could really hurt ourselves with a few bad scripts. We cannot just use C as a script language without first jumping through a few hoops. It does have a serious limitation that we will illustrate with the following scenario. Let us say that a C function represents each script. The pseudo-code to call our C functions looks like the following. main() { script1(); // Call Script 1 script2(); // Call Script 2 script3(); // Call Script 3 }
In C we are only able to run these scripts sequentially from start to finish. This does not do us any good. Since almost all of our scripts need to run over time during the course of the game. The following is a pseudo-code example of a script that describes an explosion effect. Script_Explosion() { Create A Smoke Particle System Create Explosion Sprite Wait 1 Second Create 2nd Explosion Sprite Move 2nd Explosion Sprite Up 100 Units Over 3 Seconds Wait 3 Seconds Destroy Explosion Sprites Destroy Smoke Particles } In order to run this script, we will need to run the script for a fixed time, draw a frame, run the script some more, and draw another frame etc. until our script terminates.
The simple solution would to create a separate thread for each script. However, threads are not practical due to their operational overhead and performance constraints. What we want is something that behaves like a thread but is more lightweight so it can support a large number of scripts. This brings us right back to the original topic of co-routines. Think of them it as a simple co-operative multi-tasking mechanism. They are very much like a thread except they run in a pseudo-parallel manner. Each co-routine represents a path of execution: they are able to stop after a certain time, and later continue where they left off. Unlike threads each script only runs by manually receiving control of the CPU, they must then manually give up control of the CPU at specific points. This suits us just fine, since we do not want them to run all the time and interfere with the rest of the finely tuned code in our game. Just as long as each co-routine gets their CPU time between a render-frame, we are good. Each C script represents a function and runs as a separate co-routine. |
|