Showing posts with label c++. Show all posts
Showing posts with label c++. Show all posts

Sunday, 7 October 2012

Video Game in C: using X macros to define game data

As my brother and I recently released our last video game voxel invaders on android and symbian, I though I would share some of the technical details about it. For my the first post I will talk about a quite useful, although rarely used, C trick known as "X macros", and how we can use it to simplify game code.

The code is written in plain C, and the original design was very simple: all the elements of the game are stored in a structure (called obj_t in the code) that looks like that:

struct obj_t { 
    int type;
    float pos[3];
    float speed[3];
    sprite_t *sprite;
    // A lot of other attributes follow...
};

The important attribute of the structure is the first one: int type. This value allows us to differentiate all the kinds of objects in the game (in C++ I would have probably used subclassing instead). We can see it as a pointer to the object class, except that it is not a real pointer but an index on an array of a structure obj_info_t that contains all the information about a type of object (the game equivalent of a C++ class).

file objects.h:

struct obj_info_t {
   const char *sprite_file;
   float initial_speed[3];
   void (*on_hit)();
   // Lot of other attributes...
};

enum {
   PLAYER,
   ENEMY_A,
   ENEMY_B,
   // And so on...
   OBJ_COUNT
};

file objects.c:

obj_info_t obj_infos[] = {
    // PLAYER
    {
        "data/img1.png",  // sprite_file
        {0, 1, 0},        // initial speed
        NULL             // on hit
    },
    // ENEMY_A
    {
        "data/img2.png",  // sprite_file
        {1, 2, 2},        // initial speed
        enemy_a_on_hit    // on hit
    },
    // And so on..
};

By the way, this kind of design was mostly inspired by the code of the original doom game by John Carmack.

The first improvement we can do is to realize that since we are using C98, we can make the array declaration look better using designated initializers:

file objects.c:

obj_info_t obj_infos[] = {
    [PLAYER] = {
        .sprite_file = "data/img1.png",
        .initial_speed = {0, 1, 0},
    },
    [ENEMY_A] = {
        .sprite_file = "data/img2.png",
        .initial_speed = {1, 2, 2},
        .on_hit = enemy_a_on_hit,
    },
    // And so on.
};

See how the code already looks nice and simple. Although this is how our code looked like for a while, at some point we started to get annoyed by a problem with this pattern. The problem is that every time we define a new enemy, we need to modify too files: the file containing the object types enum, and the file containing the object infos array. Beside, since there is no way to separate the array or the enum into several files, those two files got bigger and bigger. This might not seem too bad, but really it is, specially when you have to find the definition of a given object in the thousand of lines of code containing the obj_infos array.

As I mentioned, the original doom engine also used this kind of pattern, and I think the way they overcame this problem was to use a special tool that would automatically generate the C code for both the enum and the array.

In our case I though writing a C generator tool would be overkill. That is where I realized that there is a simple way to have the C preprocessor generates those two parts (enum and global array) for us. Later when I searched for occurrences of this pattern online I found out this is known as "X macros", there is a very comprehensive article about it from Randy Meyers.

The idea behind C macros is to use a C preprocessor macro that, depending on the context, will expand to either the enum part, either the array initializer part.

In our simple case, it would be something like this:

file object_defs.h:

OBJ(PLAYER,
    .sprite_file = "data/img1.png",
    .initial_speed = {0, 1, -},
)

OBJ(ENEMY_A,
    .sprite_file = "data/img2/png",
    .initial_speed = {1, 2, 2},
    .on_hit = enemy_a_on_hit,
)

file object.h:

#define OBJ(name, ...) name,

enum {
    #include "object_defs.h"
}

file objects.c:

#define OBJ(name, ...) [name] = {__VA_ARGS__},

obj_info_t obj_infos[] = {
    #include "object_defs.h"
}

And so, thanks to this trick, we just need to modify the objects_def.h file to add or remove an object type. Both our enum and our global array will be automatically updated by the preprocessor at compile time. As a bonus, this makes it easy to split the object definitions into several files. For that we just need to #include all the needed files instead of just object_defs.h.

Saturday, 14 March 2009

Coroutines in C

this article by Simon tathan is fascinating.

Simon explains how we can use some very dirty tricks to create coroutines directly in C, without using the setcontext functions.
It relies on a very strange feature of C that I didn't know about : being able to put a case statement inside a sub-block of the matching switch statement !

I really recommend the lecture of the article to anybody interested in coroutines, even though as Simon says : "this trick violates every coding standard in the book. Try doing this in your company's code and you will probably be subject to a stern telling off if not disciplinary action!"

Sunday, 18 May 2008

Calling Vala code from Python

Today I gave a try at using vala and python together in my OpenMoko mobile phone.
Python is a powerful object oriented script language, and vala is a C gobject compiler that makes it easy to generate object oriented code in C. It is quite easy to mix python and vala in a same programme, allowing us to write very quickly modular and fast applications.

So here is how it works :

Let say I have a vala module, that defines one class, with one public method. Here is the file (test.vala) :

using GLib;

namespace Test {
public class Test : Object {
public int sum(int x, int y) {
return x + y;
}
}
}

I can generate the C/gobject code for this module, using vala :

valac -C test.vala

this command will create test.c and test.h. These files are conform C and can be compiled with gcc (in fact these are the files people using gobject in C would write. If we have a look at it, we understand the pain that it is to write gobject code in C !).
But to be able to call the functions from python, we need to create wrapper to the python C interface. This can be easily done using some tools (included in the debian pygtk-dev package). For more information, see this page.

Here are the commands that generate our C python interface :

python /usr/share/pygtk/2.0/codegen/h2def.py test.h > test.defs
pygtk-codegen-2.0 -o test.override -p test test.defs > test_wrap.c

Where 'test.override' is a compulsory file that looks like this :

%%
headers
#include
#include "pygobject.h"
#include "test.h"
%%
modulename test
%%
import gobject.GObject as PyGObject_Type
%%
ignore-glob
*_get_type
%%

Alright we now have a file called test_wrap.c, that contains all the wrapper functions from the python C interface to gobject C.

The next step is to actually create the C library callable from python. this library must contains a function 'inittest' (this is the function that will be called when we import the module from python.) The file looks like this (test_module.c) :

#include

void test_register_classes (PyObject *d);
extern PyMethodDef test_functions[];

DL_EXPORT(void)
inittest(void)
{
PyObject *m, *d;
init_pygobject();
m = Py_InitModule("test", test_functions);
d = PyModule_GetDict(m);
test_register_classes(d);
if (PyErr_Occurred ()) {
Py_FatalError ("can't initialise module test");
}
}

And then we are almost done... finally we compile and link everything :

CFLAGS="`pkg-config --cflags gtk+-2.0 pygtk-2.0` -I/usr/include/python2.5/ -I."
LDFLAGS=`pkg-config --libs gtk+-2.0 pygtk-2.0`
gcc -c -o test.o test.c
gcc $CFLAGS -c test_wrap.c -o test_wrap.o
gcc $CFLAGS -c test_module.c -o test_module.o
gcc $LDFLAGS -shared test.o test_wrap.o test_module.o -o test.so

And now we finally have our library : test.so, that can be directly used from python. Here is our python script (test.py):

import test
t = test.Test()
print t.sum(2,3)

Conclusion :
This combination (python + vala) is ideal to write applications for OpenMoko : there is no need to compile the python part, and since the vala code generate C code, it can be easily compiled for different targets.
So no excuses not to write great apps for OpenMoko :)

Wednesday, 30 April 2008

How to compute "y = a * x + y"

I recently tried to write a numerical computation software that needed to perform a lot of vector operations. My first idea was to use python, and the numpy library.
But then I found that this simple operation :

y = a * x + y

Where x and y are large numpy arrays, and a is a scalar,
was much slower than the same thing coded in C, like this :

void func(float x[N], float y[N], float a) {
for(size_t i = 0; i < N; ++i) {
y[i] += a * x[i];
}
}

The reason is that numpy has to create a temporary object to store the result of the multiplication, and then also create a new object to store the result of the addition. When the size of the arrays becomes large - I was dealing with several Megabytes data arrays - it can make a lot of difference.

But I don't like the C code either, because of the very ugly loop.

So what can I do ? Well, first I could use the C blas library, then the operation is replaced by a function call, like this :

cblas_daxpy(N, a, (double*)x, 1, (double*)y, 1);

But it still looks over complicated.

OK maybe some other languages can be more suitable for this task. What about C++ ? With the boost::ublas library, it looks already much better :

using namespace boost::numeric::ublas;
void func(const vector& x, vector& y, double a) {
y += a * x;
}

Well, almost what I wanted, I could even use template parameters to make the function accepts any kind of input data.

But since at the end I want to be able to call this function from python, C++ is not the easiest choice (beside, I don't like C++ that much). Finally I used a language I really don't like, but that appeared to be the best for this particular task : Fortran90

Fortran90 is already much better than the horrible Fortran77 (That nobody should ever use). and most important, it has vector operations included in the language.

So now my simple operation looks like this :

subroutine func(x, y, a)
implicit none
REAL, dimension(:), intent(in) :: x
REAL, dimension(:), intent(inout) :: y
REAL, intent(in) :: a

y = a * x + y

end subroutine func

(Note : in Fortran90, you DON'T need to start every lines at the 7th character !)

I can call this fortran function from python quite easily, thanks to F2PY, and voilà. Then I can extend my function to perform all sort of numerical operations.

Conclusion : Fortran90 is not as bad as I though it was, combined with python it can even be quite useful. But I still don't like this language, I am looking forward for the D language to have good numerical operations library, as well as python wrapping (it already exists, but there are still a few issues.)