December 28, 2004

The key benefit of C# for game code

I am a little unsatisfied with the answer I gave to a comment on my previous post. It was somewhat abstract and hand-wavy. So I thought a concrete example might be a good idea.

The reason I am writing a C# to C++ translator prototype is because there is one feature missing from C++ that I think would be extremely beneficial for game development. That feature is the ability for a program to examine its own structure, or the structure of another program, through reflection. This feature is not unique to C#. But I'm not trying to start a revolution here. I want a language that is very similar to C++ that has this additional feature. C# is ideal in this regard.

As a concrete example, consider the way most games tie together a game's asset editing tools, the assets themselves and the game code. We have some tools: level editors, modeling tools, animation tools, texturing tools, etc. And we have a game that needs to load the assets produced by those tools. In order to accomplish this we need various data formats known to both the game and the tools. The tools need to be able to output these formats and the game needs to be able to load these formats.

An effective practice is to have an intermediate format description language. These have become common place in recent years and are key to the idea of data-driven development. They are typically some kind of structured text file, often XML, that describes a tree of data that both the tools and the game understand.

One of the advantages is that they decouple the game from the tools so that a change in one does not require an immediate change in the other. It allows the various teams to work on slightly different timelines, thus eliminating task dependencies. Another advantage is that they can contain other useful stuff, such as how a the information should be displayed to a designer or artist so that they can edit it. They can also contain information about legacy versions of the format to allow backwards compatibility. All good stuff.

We do not use format description languages for all our formats. But that does not mean they would not all benefit from one. How often do we here a programmer say to an artist, "Yeah you need to re-export the file" ?

However, these schema languages are only necessary because C++ is incapable of reflection. Unlike C++ programs, C# programs are also schemas. A game written in C# would have no need for a separate format description language. Here is a concrete example. Suppose we have a game where an avatar walks around a maze collecting fruit. We might express the level format using a schema like this:

[levelSchema]

[element name="Fruit" abstract="true"]
[attribute name="Position" type="Vector2" default="0,0"
edittingTool="DragAndDrop"/]
[attribute name="Points" type="int" default="1"/]
[/element]
[element name="Apple" extends="Fruit"]
[attribute name="Speedup" type="float" default="1.5"/]
[/element]
[/levelSchema]


Okay I admit it won't win any contests for best schema language but it serves as a simple example. In a C++ game, we would most likely have a object model that followed the structure of the format and some code to load data of that format. The code might be written by hand or somehow automatically generated. Whenever a programmer wanted to change the object model or the format, they would update the schema, the object model and the loading code. If they are lucky they won't need to change the tools. That's a key advantage of using a schema language.

So how does C# help us? In C#, the game code might look like this:

[GameObject]

public abstract class Fruit
{
[Edittable(true)]
[Default(1)]
public int Points;

[Edittable(true)]
[Default(Vector2(0, 0))]
[EdittingTool("DragAndDrop")]
public Vector2 Position;
}

[GameObject]
public class Apple : Fruit
{
[Edittable(true)]
[Default(1.5f)]
public float SpeedUp;
}


This is contrived but, as you can see, the schema is embedded in the C# code, which eliminates the redundancy of expressing the format in both C++ (in the object model and the loading code) and in the schema. Programmers have less work to do and there is less possibility of error or things getting out-of-sync.

Furthermore, because the program can examine it's own structure, i.e. examine the schema embedded in itself, it can automatically determine how to load data of the format described by the schema. Alternatively, an offline compiler plugin can examine the structure of a partially compiled game and automatically generate all the loading code.

Additionally, in the same way as a compiler plugin can examine compiled code, a tool can simply load the compiled game as an input file and deduce what the file format is. Alternatively, if the tool and the game are connected over a network so that a designer can edit the level as the game is running, the game can simply use the reflection API to query it's schema and send it to the tool just after the network connection is established. There is no need for the tool to know anything about the game.

There are many other ways reflection could be used effectively. For example, it might be used to automatically generate network protocols. Certain fields could be annotated in the schema to indicate how they are synchronized.

What appeals to me most is it would make things just work. Imagine, you add two lines of code to your game:

[Edittable]

public float MaximumSpeed;


In C++, all this would do is add a member variable to a class. In C#, in addition to this, it automatically introduces a new GUI element into a designers level editor, extends the level file format and automatically generates all the loading and saving code for the new field.

Don't Repeat Yourself!


Comments: Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?