Moving, Shooting and Formations

Since last time I have added path finding, shooting and formation movement. All three new aspects are in still in alpha trials, so they will probably change a lot before the game is finished. Here’s a video:

As you can see I’ve added in some enemy ships in red. If you are ever flying a red spaceship and you are shooting at a green spaceship, you should think long and hard about who the bad guys are…

Irritatingly the video jumps at 0:06, again this isn’t what I see on the game, and is something to do with adb’s recording. Perhaps I’m using it wrong? This one has been recorded at 4Mbps.

The path finding algorithm I have used is called A*, I’m going to write an article or two on how it works and how I implemented it, so stay tuned. At the moment when more than one unit is moved at the same time, they will stay in the formation that they were in when they were given the move order.

I spent a while with the shooting, and I think that I have already changed my mind on how I’m going to do it. I think I will reserve visible projectiles, like the ones seen in the video above, for torpedoes and other large ordinance. For the kind of guns/lasers that fighters and the like have, I think I’ll just animate the guns and the effects. We’ll see. One of the reasons the shooting took me so long, is that my first implementation was hideously slow, it was drastically reducing the frame rate. At first, as I had made no attempt to do any optimisation, I thought this might be due to the large number of loops inside loops. However, looking at LogCat, I could see that the Garbage Collector (GC) was running several times a second.

12-18 08:59:35.022: D/dalvikvm(1005): GC_CONCURRENT freed 487K, 11% free 8828K/9860K, paused 4ms+6ms, total 38ms
12-18 09:32:43.682: D/dalvikvm(10131): GC_FOR_ALLOC freed 512K, 7% free 8082K/8636K, paused 26ms, total 26ms

I have read that the GC is often the cause of performance issues, so before any other optimisation, I thought I’d attack from that angle.

It turns out the cause of the problem was that I was returning float arrays from several static methods, and each call was creating a new float array, that was then subsequently GCd. Here’s an example with a function that rotates an arbitrary number of points about a point:

public static float [] rotatePointsAboutAPoint(float [] points, float [] rotateCenter, float angle) {

   float s = (float) Math.sin(Math.toRadians(angle));
   float c = (float) Math.cos(Math.toRadians(angle));

   float [] out = new float [points.length];

   int round = 0;

   for(int i = 0; i < points.length / 2; ++i) {
       round = i * 2;

       out[round + X] = c * (points[round + X] - rotateCenter[X]) - s * (points[round + Y] - rotateCenter[Y]) + rotateCenter[X];
       out[round + Y] = s * (points[round + X] - rotateCenter[X]) + c * (points[round + Y] - rotateCenter[Y]) + rotateCenter[Y];
   }

   return out;
}

This function is called when checking if one ship is within range of another, which happens ALL THE TIME. So you can see how a stupid number of temp variables would be created. My solution at this point, is to make those variables static, like this:

/*
 * WARNING
 * 
 * The following static variables are global to this class, but should be treated as uninitialised
 * They will be in the state that they were in before, which could cause some difficult errors
 * 
 * They are like this to prevent the GC from running, which drastically decreased performance
 * 
 */
// rotatePointsAboutAPoint
private static float [] OUT_COORDS_1 = new float [2];
private static float [] OUT_COORDS_4 = new float [8];
private static float SIN_THETA, COS_THETA;
private static int ROUND;

public static float [] rotatePointsAboutAPoint(float [] points, float [] rotateCenter, float angle) {

    SIN_THETA = (float) Math.sin(Math.toRadians(angle));
    COS_THETA = (float) Math.cos(Math.toRadians(angle));

    ROUND = 0;
    for(int i = 0; i < points.length / 2; ++i) {
        ROUND = i * 2;
        OUT_COORDS_4[ROUND + X] = COS_THETA * (points[ROUND + X] - rotateCenter[X]) - SIN_THETA * (points[ROUND + Y] - rotateCenter[Y]) + rotateCenter[X];
        OUT_COORDS_4[ROUND + Y] = SIN_THETA * (points[ROUND + X] - rotateCenter[X]) + COS_THETA * (points[ROUND + Y] - rotateCenter[Y]) + rotateCenter[Y];
    }

    switch(points.length) {
    case 2:
        OUT_COORDS_1[0] = OUT_COORDS_4[0];
        OUT_COORDS_1[1] = OUT_COORDS_4[1];
        return OUT_COORDS_1;
    case 8:
        return OUT_COORDS_4;
    default:
        return null;
    }
}

This doesn’t look nice does it? Firstly if I ever forgot to reset any of those variables for any reason, they would be stuck at the value from the last calculation, which would cause some wacky, deceptive errors. Also the function is less versatile as it no longer works with an arbitrary number of points. That’s not so bad as so far I only use this for rotating points and rectangles, but still…

Have you encountered a better way of doing this?

After doing that, the game runs smoothly, as you can see (excluding the jumps…). Lots of people complain about Java’s GC, lots of other languages, including C++, don’t have one. At the moment I am glad it exists, as otherwise I would be spending my time battling memory leaks as well, but perhaps it will annoy me in the future.