This article is a departure from previous ones, as it’s aimed to other developers, hobbyists, and curious people.
It’s a technical article, some kind of “behind the scenes”.
The goal of this article is not to be theoretical or generalist, there’s already plenty of these on the Web. I will rather talk about our experience with scripting languages.
How it works at D2P Games
We made the early choice to use Lua as a scripting language. That wasn’t a very difficult choice, there’s a lot of good solutions available and Lua is really well suited for video games. It’s used in a lot of commercial games and is fast, robust and well documented.
I already used Lua for some years and I didn’t find any good reason to use something else.
The addition of Luabind was a huge time saver, as it takes care of the binding for us.
So how do we use Lua ? We use it for almost everything !
We have a C++ codebase, based on SFML and OpenGL. This codebase is very, very low level. It provides a set of functions like “LoadImage”, “DrawImage”, “PlaySound”, etc.
The implementation is object-oriented, but the final interface is not. Resources are handled using simple numbers passed across the different functions.
It really eases the binding with Lua.
So if the C++ engine provides only low level functions, we need to implement ALL the game logic in Lua.
There are pros and cons.
- Productivity. We are only three people working two days per week, every gain in development time is welcomed.
We don’t want to lose time with memory management, header/implementation separation, etc.
It keeps compilation times away from us : we just need to change the code, then relaunch the game. No time wasted waiting, it’s really useful when you want to quickly change some parameters to try some of your ideas.
Getting rid of large amounts of code is also easier because you spent less time to write it. No more “I won’t rewrite this part because it took me so long to write in the first place”, just press delete and move on to the next task.
- It allow us to be messy. Yes, it may sounds like a strange advice. I’m sure all your teachers have told you not to write messy code but to do careful planning, draw UML diagrams and bla bla bla blaaa… But in the end, you have to get the job done. The good programmer is not the one that follows all the guidelines, but the one that ships his product with all the planned features, no critical bug, and in time.
The only real and universal advice that can apply for programmers is : “choose the right tools for the job”. It also works with methodology and processes.
We follow an iterating process, similar to agile methods : the goal is to get a working version as soon as possible, and then keep on iterating on it, adding (or removing) features.
This way, planning our code or documenting it is very time consuming, it would slow us down too much. Considering 90% of the code will be refactored anyway, it just doesn’t worth it.
Don’t get me wrong, you still have to follow some internal guidelines and write readable code with comments and meaningful variables’ names. I’m just saying you don’t have to bother with strict OOP principles : keep it simple and working. And do it quickly.
- Scripting languages are simple. With few features and verbose syntax. Learning them shouldn’t be a problem for a designer with no prior technical background.
Though it’s not the case for us as we are three computer-science students with some affinity for programming.
- It may not be obvious but the dynamic aspect of these languages makes them hard to debug : all the errors are going to be runtime errors. If you passed the arguments in the wrong order, you won’t know until this specific code is executed, which may be a rare scenario. It’s vital to test the game often, and fully.
Otherwise you will be like “argh, the game crashed, but don’t worry it’s nothing, I will fix it right now” during a playtest (true story by the way). It may be okay for a playtest, but it’s unacceptable for a public release.
- The topic of performances is often debated. The only truth is that it depends of the game. We experienced performance issues with Reckless Squad, but it’s a real-time strategy game, with a lot of units that need to run relatively-complicated AI and this lead to a huge CPU charge.
Moving the pathfinding (A*) and neighbor units queries from Lua to C++ did help a lot to solve those problems.
But don’t do it until you’re really sure it’s your bottleneck. Trying to optimize too early is generally a bad idea.
Reckless Squad is still far from being finished, but we already learned some important lessons.
- The first one is that using a scripting language has advantages that far outweigh the inconveniences. So it’s worth it.
- The second one is that you should keep your binding layer thin. Make your C++ code higher level if you need to, but expose the fewest functions possible to the scripting language.
- Finally, be very careful about what you do, because the flexibility of the scripting languages made them harder to debug.