C++ vs C#: has the time come to develop games in a managed language?
(level : easy)
In December 2006, Microsoft released the first version of the
XNA framework and tools,
designed to allow programmers of all levels to develop games for both Windows
and the Xbox360. Initially, the only language supported is C#, and if others
(like Visual Basic) are added in the next versions of XNA, they will all belong
to the .NET family, meaning they will all be managed languages. This makes
sense for Microsoft, since they need some way of controlling what is executed
on the Xbox360 especially, to prevent people from creating viruses, cheating on
Xbox Live, or running pirated games. But from a developer's point of view, how
adequate is it to use C# to write games, what are the advantages and drawbacks
of the managed language compared to the good old native C++? (in this article,
"C++" always refers to "native C++", and not "managed C++" of course).
C++ is the language of choice to develop console games, isn't it?
Yes it is, if we're talking about game engines and middlewares only. But creating a game
requires tools, lots of them, some you buy and some you develop yourself. A
quick survey during a Tools Roundtable at GDC 2006 showed most game companies
use C# and the Windows Forms to write new internal software, and sometimes even
try to progressively migrate their old C++ / MFC applications to the .NET
framework. They don't do it purely for fun of course, but because they found
their teams work faster and can accomplish more when using .NET (and the
programs eventually end up being more reliable - more on that later). At Alias'
Maya API developer's conference in June 2006, people from
Secret Level gave a talk
about using C# to develop Maya plugins, despite the fact the SDK is 100% C++
based, and reported their productivity was multiplied by a factor of around 3.
What about the game code itself, responsible for
everything happening in the universe presented to the players? Most of the time
it is written in some scripting language, like Lua or UnrealScript. Initially,
it used to be like that because technical designers were the ones creating
behaviors for the different entities of the game world, and they needed
something they could modify and test without recompiling / debugging the whole
project in Visual Studio or something equivalent. Guess what: game programmers
have the same problem now, at High Moon Studios
[1]
they don't implement features requested by the designers in C++ anymore, they
use scripts, and the explanation for that is iteration time.
[1] The views and opinions expressed in
this article are strictly mine, and don't necessarily reflect those of High
Moon Studios (my current employer) or Vivendi Games.
Iteration time
As everybody knows, on average projects keep getting bigger in the game industry,
with more programmers writing more code and using more middlewares (I'm not
even talking about content). This results in tens of thousands of lines to
compile, and even the best compilers are having a hard time dealing with that
amount of C++ code. One problem here is header files: parsing them takes a lot
of time, and if precompiled headers are helpful for external libraries such as
the STL, they don't solve anything regarding code that is frequently modified.
The issue is actually so big, that some people use the
Single Compilation Unit method
to work around it: the basic idea is to have a new .cpp file that includes all
your original .cpp files (you read that correctly) and nothing else, and to
compile this single unit instead of all the separate files. This technique has
some drawbacks (no more incremental builds, potential naming conflicts, some
definitions are now visible from everywhere, see
this page
for details), but I tried it for myself, and got a 4.6x speed improvement on
some not so big (about 100k lines) code base!
C# doesn't have header files. When you think about it, this is pretty cool, since this
basically divides by 2 the number of files in your solution, and you don't have
to repeat prototypes of functions you're going to implement somewhere anyway.
In addition to that, your code is only compiled to some platform independent
intermediate language (MSIL), which simplifies the job of the compiler, and
doesn't have to be linked since C# uses DLLs rather than static libraries. Of
course this has some implications regarding performance when you launch your
program, but the bottom line is: compiling is very fast.
To me, the biggest advantage of using C# (or another managed language) is not the huge
number of classes provided by the .NET framework, or the fact that it handles
the destruction of unused objects for us through garbage collection, or how much
easier it is to do some things with Forms rather than MFC; it is iteration
time, i.e. the time it takes to modify, compile, and test some piece of code.
If this time is too long, and you have to wait a few minutes before you can see
the effect of each little change you make to your program, your productivity
drops dramatically as you end up spending more time waiting than typing. Like I
mentioned before, the programmers working on gameplay features at High Moon
Studios exclusively use scripting, because it allows them to try many more
things very quickly, without having to go through the long C++ compiling and
linking process. The idea is the same when using C#: compiling is fast, and you
can get a lot more done than if you constantly have to wait for feedback.
Don't get me wrong though: I'm not saying it's
impossible to have a C++ code base made of loosely coupled classes, where
inter-dependencies are kept to a minimum, and concepts such as
interfaces
and the Pimpl idiom
are put to good use to improve compilation speed. But the truth is: the C++
language doesn't do anything to help you in this area, getting things right
requires constant attention, and we all know unnecessary headers always end up
being included in other headers at some point (this is actually the most common
mistake I find when reviewing code samples from job candidates).
Reliability
Iteration time is one thing, but the productivity of programmers is certainly also
greatly affected by bugs: the more time we spend debugging, the less time we
have left to implement new features or extend existing ones. Everybody makes
mistakes, but some of the characteristics of C# help preventing very common
ones:
-
uninitialized variables
In C++, you can create variables without giving
them any initial value. Even worse, your program may still work in debug mode,
because the compiler will assign the NULL value to pointers, and this case is
tested in your code, for example. It's only in release mode that your
application will start crashing, maybe not in a very reproducible way if you're
unlucky, and who wants to debug in release mode?
In C#, every variable has a defined value. Most
of the time you'll have to assign one when creating a variable, or the the
default one corresponding to the type of the variable will be assigned (members
of a class, for example), or the compiler will report an error as soon as you
try to use the variable (local Int32 variable in a function, for example).
-
memory leaks
The managed languages of .NET support "garbage
collection", which basically means they take care of releasing the memory
occupied by objects that are not used anymore. Be careful however: if your program
keeps a reference to the root of a big tree of nodes, for the whole lifetime of
the application, the tree will not go away magically; it still is your
responsibility in this case to say you don't need it anymore. When is garbage
collection useful then, if we keep having to indicate what we want to get rid
of? There are two main cases I can think of:
- when a reference goes out of scope, the object it represents will be deleted (some
time in the future, not necessarily immediately), provided it is not used in
any other place (it is more or less like using smart pointers without knowing
it).
- when a reference is assigned a new value - in C++, you would get a memory leak if you
forget to delete the object that was previously pointed to by a pointer, before
assigning a new address to that pointer.
The bottom line is: in C#, you don't have to write
'delete', or 'delete[]' for arrays (another potential source of error in C++!).
-
strings
Strings are a mess in C++, aren't they? We can
choose to use char*, TCHAR*, std::string, our own custom class, you name it. In
different code bases I've seen, several of these types are actually combined,
and converted back and forth all the time.
Nothing prevents you from writing your own string
class in C#, but it's easier to just use System.String (or 'string' for short),
which has a pretty good interface. One thing to note is that C# strings are
reference counted, meaning you will only have one instance of a given string in
memory, even if you create it 10 times in different places. That's why
functions allowing you to manipulate strings don't actually modify them: they
return an instance of the new string, without changing the original one.
-
pointer arithmetic
There are no pointers in C#, only references. How does that help writing less bugs? The answer is
pointer arithmetic: in C++, it is way too easy to add any value to a pointer,
and end up with an address corresponding to some random memory you may even not
be allowed to access. Or to overwrite something just before or just after the
object you wanted to touch. This is the source of many many bugs, usually quite
difficult to locate, and surprisingly enough, coding without pointer arithmetic
is not annoying or difficult; it is much less difficult than learning about
C/C++ pointers in the first place!
-
multiple inheritance
Everybody knows multiple inheritance is
dangerous, and should be avoided except in very special cases (see More
Exceptional C++ from Herb Sutter, for example). Guess what: in C#, a class can
only inherit from one base class, there is no choice. But it can also implement
as many interfaces as you want, or you would only be able to write quite simple
programs. Basically, this means a class can only inherit member variables from
one other class, since interfaces don't have any. Of course, the same design
can be implemented in C++ with pure virtual classes, it is just more convenient
and less error prone in C# since there is an 'interface' keyword to:
1) force all the functions to be public and pure (= not implemented in the interface itself)
2) ensure there is no data member in the interface
3) verify every function is supported by the class implementing the interface
And once more, there is simply no multiple inheritance in C#.
-
exceptions
Exceptions do exist in
C++, they are just not used very often in game development, because they're
known to be quite expensive performance-wise. But they are mandatory in C#:
this is how functions from the framework will let you know when they
encountered a problem (for example, trying to delete a read only file throws an
exception). And you are encouraged to do the same with your own classes,
instead of propagating error codes through the call stack, until some function
decides what to do with them... or not. There are several reasons why exceptions
should be prefered to error codes, like the fact that you won't be able to
ignore them (no "or not" here), and that you won't have to modify the
prototypes of your functions to add error handling. But basically all I'm
saying here is: if like me you never used exceptions in C++, you'll become used
to them quickly with C#, and should get convinced this is a better approach
than error codes propagation. Obviously, exceptions should stay exceptional in
anything but tools, but this doesn't mean you shouldn't be ready (= have some
code) to handle them, as long as they're not thrown they shouldn't cost you too
much with C# (see
this page).
-
configuration settings
There are tons of
settings in a C++ project, and making different libraries or middlewares work
together can sometimes turn into a nightmare. Changing a few settings in
several configurations of all your projects can also be time consuming and
error prone, even if you can write a script for that task. C# simplifies things
a lot, since it does not have that many settings; some people might complain
about the loss of flexibility this implies, I personally won't!
-
macros
Macros are evil, every serious book about C++
will tell you that. They're easy to get wrong, impossible to debug, and can
most of the time be replaced (in C++) with constants, inline functions, and
templates. No macros in C#, period.
-
things you don't want the compiler to guess
The C++ compiler is
usually your friend, and it will try to do its best with the information you provide
it through your code. Sometimes, that means it will perform some operations
under the hood, like implicit conversions for example, when it thinks that's
what you intended to do. On the opposite, the C# compiler is not that
forgiving: you have to tell it exactly what you want to do, all the time, or it
will complain. You want to make all your member variables public? You have to
use the 'public' keyword for each of them, you can not just change the access
rights of a whole block like in C++ (and everything is private by default). You
want to override a virtual function in your derived class? Use the 'override'
keyword. You want to pass an integer parameter by reference when calling a
function, so that you can get a modified value after the call? Obviously this
function needs to have such a reference in its signature, but you also have to
specify you really want to use a reference when making the call [by writing
something like: result = MyFunction(ref someInteger); ]. Sure, this is a bit
more typing; but having the compiler force you to describe very clearly what
you're doing is actually a good thing, after all you're the programmer and you
don't want the compiler to try to guess what you meant.
I only listed a few
things here that, in my opinion, make it a bit easier to avoid very common bugs
when using C# rather than C++. This list is not exhaustive, I didn't talk about
type safety and delegates for example (delegates is the C# way of having type
safe function pointers basically). And this is not a summary of differences
between C++ and C# either, there would be many more (C# has 'properties',
'generics', 'reflection', but C++ has 'templates', etc).
Performance
So far in this article, I've said that C++ is not the only language used in game
development, that iteration time is critical and C# does very well in this
area, and that managed code should also require less debugging. But if you want
to go beyond tools programming, and develop a full game engine in C#,
performance is a topic of utmost importance.
The first thing people seem to worry about is garbage collection (GC): is it not too time
consuming, is the framerate going to drop if you do a GC each frame, or if you
don't call it that often and let dead objects accumulate, then will the game
stop for 1 or 2 seconds when the cleanup finally happens? I didn't do any big
experimentation myself, but here are a few interesting facts:
-
Unreal Engine implements GC,
and Tim Sweeney from Epic thinks "garbage collection should be the only option"
for the next mainstream programming language
(page 57). The engine is written in C++, and the fact that it uses GC doesn't
mean this kind of technology has no CPU cost, but many many games have been and
are using Epic's middleware, and I can not say they're especially slow because
of the GC system.
-
GC is not a new idea, it
has been researched for a long time, and the people who created the .NET
framework are aware of the concerns programmers always have about performance.
In particular, they know it could be a deal breaker regarding running C#/XNA
applications on the xbox360, so they wrote
this page
to explain how to reduce
the overhead of GC.
-
Even in native C++,
allocating and deallocating many objects each frame is pretty bad practice,
that can lead to memory fragmentation if these objects have very different
sizes. What is usually prefered is to allocate all the static objects (the
ones that don't get destroyed for the whole lifetime of a level, for example)
first, if possible in some contiguous block of memory, and to try to recycle
entities stored in various memory pools for dynamic objects (such as streaming
textures, particles, etc). The more you do this, the less GC is a problem.
Another issue is Just In
Time (JIT) compilation, and the speed of the generated code. JIT compilation
means a big part of your code is going to be translated from the Microsoft
intermediate language (MSIL) into native code for the local machine at startup,
when you launch your application. This works pretty fast, but some other
assembly that is not needed immediately could also be compiled while your
program is running, in the middle of the action, right before the code it
contains gets executed. I haven't tried it on the xbox360, but on PC there is a
tool called NGen
that should take care of this potential problem, by allowing you to compile
everything on the target computer once and for all, when installing the game
for example.
In the
January 2007 issue of the Game Developer magazine,
Mick West described how he recompiled a blob simulation with the /clr option of
Visual Studio, to compare the size and speed of a native application with a
corresponding managed one. He found the framerate dropped from 160fps to 60fps,
which sounds terrible even though he admitted this sample was more of a worst
case than a best case scenario for the .NET framework. Still, what I don't like
about this test (I like the article though, since it encourages people to try
managed code) is it doesn't take the specifics of the target language into
account: even if native and managed C++ look quite similar in a text editor,
the way they operate and use resources is very different, and I'm not sure
using the STL in managed code is a great idea for example. When I wrote my
first C#/XNA application, I started manually converting some C++ code I had;
but since it was full of pointers and unsafe casts, this was tricky, and I
quickly ended up rewriting the same function in a different, C# friendly way.
The point I'm trying to make here is: you can not count on the compiler to do
everything for you, that would be too easy, like for any other language
programmers interested in real time performance will have to learn the do's and
dont's.
Fortunately, there are examples to prove writing games in C# is possible, and I mean fast 3D
games, not just puzzles or that kind of thing. The German company exDream
Entertainment has developed a racing game (see pictures
here)
that should become one of the starter kits of XNA, and a few PC games in C#
before that .
The blog of their lead programmer Benjamin Nitschke
contains some very interesting information about game development using .NET, and so does the beginning of
this interview
(the rest is more XNA oriented).
GarageGames also wrote a non optimized C#
version of their xbox360 Live Arcade game Marble Blast Ultra, and were
apparently quite pleased with
the results!
Sure, these games may not have tons of objects affected by the laws of physics,
or a complex path finding, or any other CPU intensive algorithm you want to
insert here; but it doesn't mean it's impossible, it's probably only a question
of time before somebody does it.
Other performance pitfalls
I've seen mentioned are related to 'boxing' (converting a value-type, for
example an 'int', to a reference-type such as System.Object), and the Platform
Invoke (P/Invoke) mechanism (that can be used to
call functions of the Win32 API from managed code).
The basic rule here is to avoid doing these things too frequently (i.e. in time
critical loops), completely getting rid of them is most likely overkill (let's
call it premature optimization).
There are certainly a
few more problems that will show up every now and then, and performance is
probably always going to be a bit worse with managed code than it is with
native code anyway, after all nothing comes for free. Tim Sweeney wrote he
would gladly sacrifice 10% of performance for a 10% higher productivity,
personally I'm fine with losing even a bit more than that, if that allows me to
take advantage of the huge productivity gain C# brings to the table. In a way,
to me it's like using C++ instead of assembler: you can't beat the speed of the
latter, but writing and debugging a reasonably big game in assembler would be
time consuming to say the least, and I'd better spend more time adding features
and tuning the game, rather than try to make the fastest piece of software
ever, and have a hard time keeping it stable. The gap between C# and C++ is
obviously not as big as the one between C++ and assembler, what I mean is this
is the same kind of compromise we always have to make: performance versus
productivity.
Learning Curve
I can hear some people thinking: "I have been programming in C++ for many years, I'm an
expert, I don't want or don't have time to learn another language". There is no
worry here: you don't even need to read a book, C# looks so similar to C++ that
you can start coding immediately. Later on you may have to search for the exact
syntax for properties or delegates, but basically that's it, everything is
pretty straightforward (especially if you have done some Java as well).
The real problem is to
avoid reinventing the wheel, by knowing what's available in the .NET framework,
and where to find it. There are literally a few thousand classes, which might
sound overwhelming; fortunately, they're grouped under namespaces corresponding
to what they do (for example, System.Collections includes the different
containers supported by the framework, and the generics introduced by .NET 2.0
are in System.Collections.Generic), you will end up always reusing the same
ones, and the IntelliSense system of Visual Studio makes it easy to navigate
the tree of namespaces and see what classes are in there. Otherwise, there is
the internet and your favorite search engine, that works very well too :-)
If you have to create
GUI applications, using the Windows Forms shouldn't be difficult. The controls
are the same as usual, with the addition of the PropertyGrid (used in Visual
Studio itself), which is a very powerful tool since it takes advantage of the
reflection feature of C# to display the properties of one or several objects
(yes, it supports multi-selection editing!). I would just recommend using VS
2005 and .NET 2.0 rather than VS 2003 and .NET 1.1 if you have the choice, a
few missing events have been added to some controls, and the designer feels
improved as well. If you have used MFC in the past, you should find the code
generated by the designer is cleaner: for example, the horrible message maps
are replaced by event handlers and delegates, and some tasks like creating
nested splitters don't even require you to write code anymore, it can be done
entirely in design mode. Same thing with resizing controls when the dimensions
of the window containing them change: the new docking/anchor system is not
perfect, but it can handle a lot of common situations with only a few
properties to edit.
To sum it up, programming in C# is easy if you have coded in C++ for a long time, creating a
GUI with the Windows Forms is not difficult either, discovering the content of
the different namespaces of the framework takes a bit more time though; but you
don't have to learn everything upfront, this is an incremental process, and
these already implemented classes is one of the things that will help you save
time and increase your productivity.
Has the time come to develop games in C#?
If C# is so great, why
is not everybody in the game industry already using it? The first problem is:
it's not really cross platform. Its primary target was the PC, it has only been
available on xbox360 since the end of 2006 (with the release of XNA 1.0) and
there are still a lot of limitations in this case (for example, it's impossible
to write a tool running on PC that talks to the console while the game is
running), and it's not supported at all on other consoles.
The second problem is
middlewares: they are all written in native code, with a C++ API. It is totally
possible to call such an API from C#, for example to write a managed program
that uses OpenGL for rendering. There are several drawbacks to take into
consideration though:
-
Writing the wrapper translating the C# calls to C++ (because the types are not the same
in both languages, for example a C++ function can take a char* parameter) is
tedious and error prone. And it has to be done each time you get a new version
of the middleware. This can be automated to some extent, but from what I've
heard there are always some tricky cases that have to be handled manually.
-
Each call to the middleware means switching from a managed context to a native
context and back, which is expensive performance wise. This will also make
debugging more difficult in general (assuming you have the source code of the
middleware).
-
Using native libraries kind of defeats the purpose
of writing a managed application in the first place, since these libraries
won't benefit from garbage collection for example, if they allocate memory.
A third problem is executing the game: in addition to DirectX or OpenGL, a C# game
needs the .NET framework in order to run. This issue is not too bad, PC games already
contain a redistributable version of the DirectX runtime, they could also
install the .NET framework if it's missing on the target computer. On xbox360,
I suppose Microsoft could push the framework through Xbox Live if they wanted
to, but it would also have to be included on the DVD anyway (for people not
connected to Xbox Live).
So, do the first two problems mean the answer is "no" (the time has not come
to develop games in C#)? I guess if you want to develop for
other platforms than the Microsoft ones, you'll have to stick with the good old
C++, probably for a very long time (I don't see any other language replacing it
any time soon). You can (and should) still use C# to code some tools running
on PC, but that's about it.
Now if you're mostly
interested in the PC and Xbox, or if you can afford (lucky you!) to develop a
game for only one platform (and eventually port it to other ones later when
your title is successful, or have another company do the conversion), then it's
a different story. There is still the issue of middlewares, and the
limitations of XNA on the xbox360 (at least until XNA Game Studio Professional
is released, supposedly next summer), but I believe it's time to begin
experimenting, at least on PC. It's time to do some R&D work and learn the do's
and dont's regarding performance, to start writing libraries that are too game
specific to be in the framework, and perhaps to develop a prototype in managed
code for a future game. Think about it: a prototype doesn't have to run on all
existing platforms, and would be quite a learning experience! I think Microsoft
realizes developing games is really becoming way too long and expensive, and I
expect them to encourage developers to make the move to managed languages a lot
more in the future, maybe with the successor of the xbox360, or even before. If
that happens, we, game programmers, would better be ready for it! And let's
face it: learning and trying new things is part of what makes our job so
exciting, isn't it?
back to top