Gameplay in C++

Started by
8 comments, last by Codeloader_Dev 6 years, 1 month ago

Who writes gameplay in C++?

I recently wrote an article in codeproject which describes some benefits of this (from a more general purpose perspective), but I'm curious for other thoughts on the subject.

If you write engine code in C++ but the gameplay in another language, what made you take this decision?

If you do write gameplay in C++, are you happy with your decision? Share some cool stories about it.

Bonus points if you write gameplay code in C++ and engine code in another language :D 

It's only when you look at ants with a magnifying glass on a sunny day, that you realise just how often they burst into flames.

Advertisement
7 hours ago, Bleys said:

Who writes gameplay in C++?

Unreal Engine, for one.

 

Also, moving to the Lounge.

In my experience, the most common reason to write gameplay code in C++ is that you already have an engine or base framework in C++ and don't want to deal with another language.

Even if you have a well-working scripting layer, there is power and convenience in being able to write and debug the whole game from top to bottom using just a C++ IDE.

This is of course not just the case with C++ but with any language.

I write my game code in C++, engine is also C++.   If I need to allow scripting outside of IDE then I'd also still use C++ with something like Runtime Compiled C++.   

If your scripting is being done totally in-house, then I dont see much benefit of a non-engine language for the gameplay code.  You're just creating more complexity, less performance, more difficulty in maintenance, debugging, and optimization, and basically separating your programmers into two groups that will generally not interact or understand each other much of the time.

2 hours ago, 0r0d said:

If your scripting is being done totally in-house, then I dont see much benefit of a non-engine language for the gameplay code.  You're just creating more complexity, less performance, more difficulty in maintenance, debugging, and optimization, and basically separating your programmers into two groups that will generally not interact or understand each other much of the time.

The last big developer that I worked for did engine in C++, tools / data compilers in C#, and gameplay in Lua and C++. 

Lua and C# were used because they're more productive languages. Code is less complex than equivalent C++ translations, which makes maintenance easier and development/debugging quicker. Moreover different paradigms/features are available, such as duck typing, garbage collection,  mixins or the prototype pattern. Things that require silly/complex "component frameworks" in C++ can just be don't out of the box in Lua. Things like reference counting and weak pointers don't require a library :)

Most gameplay code isn't performance sensitive, but the bits that show up on the profiler would be ported to C++ and given some love. All the programmers are C++ coders who are taught Lua - you wouldn't bother hiring a Lua-only coder for a programmer's role. 

 

In my current game, we do most of the gameplay in C++, but pulling all tweakables/configuration data from Lua. Lua can be used to configure the game at a high level (plugging different components/systems together) and then the real frame-to-frame logic happens in C++. This gives us a lot of flexibility that you could get from something like ECS, without having to actually make a C++ component framework.

C++ is an extremely dangerous language though, and has to be written with care. The more projects I ship, the more I come to fear C++ for its ability to create extremely subtle yet extremely dangerous bugs, mostly revolving around memory management :( So, we use a lot of discipline around pointers to help mitigate these risks, such as templates that: tag raw pointers with ownership semantics (and do leak checks in dev builds), tag pointers with array semantics (and do range checks in dev builds), zero initialise pointers (and do uninitiated read checks in dev builds), etc, etc... We also use scope-stack allocations for most things rather than heap allocations to make leaks impossible at a semantic level. 

38 minutes ago, Hodgman said:

The last big developer that I worked for did engine in C++, tools / data compilers in C#, and gameplay in Lua and C++. 

Lua and C# were used because they're more productive languages. Code is less complex than equivalent C++ translations, which makes maintenance easier and development/debugging quicker. Moreover different paradigms/features are available, such as duck typing, garbage collection,  mixins or the prototype pattern. Things that require silly/complex "component frameworks" in C++ can just be don't out of the box in Lua. Things like reference counting and weak pointers don't require a library :)

 

The last big developer I worked for did the engine in C++ and "gameplay" in Lua, and it was a nightmare and pretty much the opposite of what you just described.  The Lua code was crap, it was hard to write so that it wasnt full of bugs, it was slow and constantly had to be ported to C++, it was difficult to maintain or even understand what it was doing, and pretty much abused at every possible opportunity.  At one point the debugger we were using stopped working because of conflicts with how the project worked (dont ask me, I'm still not sure what the problem was) and from that point on debugging amounted to using logging to find the problems.  Awesome!  The entire project would have finished with better performance, better overall code quality, happier engineers, and way faster if all that code had been C++ instead of Lua... or at least a better scripting language.

I would never willingly volunteer to ever work on Lua code again.  Yes it's easy to write the code, but it will be shit code and some obvious bugs (like, you misspelled a variable) will only show up later when that code runs, and god help you if the first time it runs is after the game ships and that code path was never tested during development.  

Most gameplay code isn't performance sensitive, but the bits that show up on the profiler would be ported to C++ and given some love. All the programmers are C++ coders who are taught Lua - you wouldn't bother hiring a Lua-only coder for a programmer's role. 

 

If you're counting on re-writing code to C++ later, that automatically kills some of the productivity gains you think Lua gave you.  Training programmer... more productivity loss.  Then you lose additional productivity debugging code across the gameplay-engine interface, or just general debugging because honestly debugging Lua code sucks bigtime.  And if you're relying on the Lua code to not be performance sensitive, then I hope you're enforcing that very strictly.  That project I was on didnt and the result was that all the high level code, as well as most of the low level code, was all done in Lua.  Oh, joy!  I wish I could get back the months of my life I wasted learning, debugging, re-writing, optimizing, and porting shitty Lua code.

Hey, if someone out there actually likes writing Lua code, awesome for them.  Go to it.  But, I would only force someone I really hated to work on Lua.

9 hours ago, 0r0d said:

The last big developer I worked for did the engine in C++ and "gameplay" in Lua, and it was a nightmare and pretty much the opposite of what you just described.

Hah, sorry for your dev-hell :(

I'd be interested to know their setup though. It sounds like whoever made that decision at this company didn't bother to flesh out the workflow and forced a terrible mess onto everyone ;(

I'd think that C was horrible if the coding environment consisted of notepad and mingw too! 

We had an IDE with full debugging (line by line stepping, locals, auto, watches, breakpoints, etc), visual studio integration, hot reloading of code, assertions with cross Lua/C call stacks, etc. Often if you hit a bug, you'd fix it while the game was still in the background, reload the code and continue running from the failed call. 

To avoid mis-naming bugs, you can modify the global-variable table to assert on writes, and should also use a good linter to catch them during compile time (most typos like that would fail the build for us, not be lurking timebombs waiting to fail at runtime). The same features that let you do breakpoints also let you collect code coverage stats, so you can tell if you're shipping code that's never been tested.

On top of that, our build server would build/lint your code, and run it on every platform with AI players to test for bugs, BEFORE allowing it to be committed to the master branch. It was pretty hard to break the build with obviously broken C++ or Lua code :)

Off the shelf C++/Lua bindings are pretty sucky, and yeah it did take us a few iterations to make our own that didn't suck, but binding is simple for us now and doesn't cause any issues. 

The vanilla VM is pretty slow (but still super fast compared to other scripting languages), so we used LuaJIT instead. On PC it's got great performance, and on console where JITing is banned, it's still 2x faster than vanilla Lua even with JIT compilation disabled. AFAIK we did have to sponsor the LuaJIT dev into supporting PPC and x64 CPUs though. We did a shitload of math in Lua and usually the only things that need to be ported are enginey-type systems, such as animation controllers, intense AI internals, etc, or stuff where you're doing math on a huge embarrassingly parallel dataset and the SIMD/threading benefits are obvious. Usually these kinds of systems were a natural fit for engine land anyway, so we ended up with a pretty natural C++ systems / Lua rules fit. 

This is off topic though - so to bring it back around, I still write C++ gameplay on my current project because I'm comfortable with it!  :DWe also use a 600Hz fixed update rate for accurate simulation on this project so I'm pretty performance sensitive... 

Another story - at the company before that one, we wrote the gameplay in C++, but the engine was mandated to have a C interface to force simplicity. Some of the engine used C++ internally in some modules, but still hid it behind a C API. The game would often then wrap up those simple C APIs in more C++ code to make them easier to use!! 

14 hours ago, 0r0d said:

The last big developer I worked for did the engine in C++ and "gameplay" in Lua, and it was a nightmare and pretty much the opposite of what you just described.  The Lua code was crap, it was hard to write so that it wasnt full of bugs, it was slow and constantly had to be ported to C++, it was difficult to maintain or even understand what it was doing, and pretty much abused at every possible opportunity.  At one point the debugger we were using stopped working because of conflicts with how the project worked (dont ask me, I'm still not sure what the problem was) and from that point on debugging amounted to using logging to find the problems.  Awesome!  The entire project would have finished with better performance, better overall code quality, happier engineers, and way faster if all that code had been C++ instead of Lua... or at least a better scripting language.

This is not too dissimilar from my own experience, actually. Better tooling would have made things a lot more manageable. So far as I'm aware, nobody had any kind of debugger available, and because of the way scripts were included in the content it was often impossible to even verify that the scripts compiled without booting up the game, never mind have working IntelliSense or refactoring tools or anything nice like that. We couldn't even do "go to definition" or the like - there wasn't any kind of unified "Lua project" where we could trace where particular symbols were defined, and different Lua files got used in different scenarios so it was a matter of searching through ALL Lua scripts for the symbol you wanted, then guessing which one was the one that would actually get used at the point where you were going to use it.

Our workflow basically boiled down to "write some Lua in a text editor, start the game, see if it worked (it probably didn't).  Writing actual gameplay code in C++ was much smoother, despite long compile times and... well, C++.

Investing in workflow and tooling is a must if you're going to use any kind of scripting language. What I take from this is that if you don't have (or your higher-ups aren't willing to put in) the resources to do that, I'd say it's better to not introduce extra languages, as they're another platform you have to support and if you're going to support something, you should support it properly. At the very least, make sure you have a debugger that works and code is searchable. If your scripting system forces me to rely on printf debugging or putting breakpoints in the C++ that is called by the scripting system, I'm going to be inclined to go back to C++ and my nice Visual Studio debugger. :P

I wrote my first game in C++. It is called Bluejay's Quest. It is right here: https://sourceforge.net/projects/bluejay-quest/
This game is the first real project that I completed from start to end. I created it in C++ instead of Java or Python because of concerns of speed. At the time C++ was probably the best way to go if you wanted something that wouldn't lag or something like that but I think I was too paranoid. Anyways, the other reason I used C++ in this case was because it was the language that I know best at the time and Java had it's issues with being ported to Linux. I wanted something that was standalone - no need to download runtime which is just a bunch of BBQ sauce.

So with scripting I had a plan and that plan became Platformisis. Platformisis is a game maker that I wrote in JavaScript so that it can run in the web browser but the original version, which was never created, was supposed to be done in C++. That is, I was to code the engine, I/O module, editors, and scripting engine in C++. This was to be full independent C++ which would even encapsulate any library I was to use so that it could be ported out to any device. Yep, it was going to be exciting but I chose JavaScript instead. So to continue with the story if I were to have built this thing I would have implemented a scripting language called C-Lesh which would talk to the engine and the I/O via the memory mapper. That can be found here: http://www.codeloader.net/readme.html Well, it's a description and tutorial of the language.

Currently, I am creating an interpreter for the C-Lesh language in C++ because C++ is fast and because C++ is a bit too bare for me. The thing about C++ is it's incredible portability. People are always talking about how Java and other stuff is super portable but (this is an important point) I say that's a crock. C++ is probably the most portable language because my different compilers have been written for various architectures and C++ has been strong for a while and it's is nothing but C with added stuff. C is ancient and is better than Basic and Fortran because I said so. But the point is portability potentially allows me to port my interpreter to any device and, thus ditch C++ for gaming. Don't get me wrong, I don't give a bucket of fried chicken or a bottle of BBQ sauce about C++ because I believe it sucks and I maintain that - end of argument but, anyways, it's portability rules and now I will create games in C-Lesh because it is everything that I want in a language.

That's my story, anyways. Just in case you're wondering I am programming Bluejay's Quest (again) in C-Lesh and under the game maker Platformisis.

Nah, this project is good experience. No one can question that.

Codeloader - Free games, stories, and articles!
If you stare at a computer for 5 minutes you might be a nerdneck!
https://www.codeloader.dev

This topic is closed to new replies.

Advertisement