Better Off Android – 1.2 Bit:GameLoop

Android-logo

Until now the ‘GameThread‘ loop I’d been using was based on the code provided by Against The Grain in their article The Game Loop

This provided a good loop, with some overtime & undertime management. Though, with fast moving games (using basic Android graphics, not OpenGL), I was experiencing excessive blurring and jittering when drawing to the screen.

This wasn’t necessarily a issue with the game loop – Since I had written these games in order to teach myself Android, using sample code and best guess work – there were always going to be issue with the code (too much garbage collection, not enough optimisation, etc). Equally, there does seem to be a problem with Android allowing background-processes to soak up too many resources from the focused app – which can cause issues – especially with scrolling games-  when too much CPU/Memory/Bus time is handed over to them. Also; I’m using AdMob which seems to be quite heavy when updating ads – Plenty of things keeping even a simple game from running well on the system.

 


 

All Android apps must face the same issues, but they don’t seem to have the same problem – so I stated googling about to see what I could find…

Theory:
GAFFER ON GAMES – Fix Your Timestep!
Game Programming Patterns – Game Loop
Bad Logic Games – Separating logic and render code

Practical:
Stack Overflow – Android 2d canvas game: FPS Jitter problem
Stack Overflow – Android smooth game loop
Against the Grain – The Game Loop
Stack Exchange – Tips for writing the main game loop?
Stack Overflow – Android fixed delta time still choppy?
Java-Gaming.org – Game loops!

 


 

I tried implementing as many of the alternatives as possible, with varying success from the results; ultimatly I settled with the variable timestep loop provided by Java-Gaming.org  – For these titles, it provided the the most consistant performance.

To smooth things out even more, I also applied a suggestion made by Jason on the Stack Overflow – Android fixed delta time still choppy? thread. He explains;

I was able to cut down quite a bit of the choopy effect that my app was displaying, I think it was mostly due to varying deltaTimes. My solution for now is to calculate the average delta time from every 5 iterations in my main loop and pass that for each update / draw call. I then continue doing this for every 5 iterations, but I also compare with the last iteration, effectively cutting in half any discrepencies between each deltaTime calculation.

With my code, I double down on this, instead of using the average delta for every 5 frames, I use the average for every 60 frames (linked directly to the games FPS variable).

 


 

A couple of final edits;

Changing Thread.Sleep with surfaceHolder.wait (A Toast timer worked pretty well too).

Using a Boolean to turn using deltaTime on/off – Scrolling games work better with deltaTime – Single screen games seemed to prefer not using it.

Separating Update from Draw – The draw method, locks the canvas, draws the frame then unlocks the canvas again  – drawing the image to the screen. Most ‘GameThread‘ samples I saw ran update after locking the screen – Which suggests this is probably the best approach to take. But I went against the grain anyway – Running update before draw, in a separate method – I thought it looked cleaner & gave me more flexibility; though if it turns out that updating after locking the canvas is a better choice, I’ll move things back in the next update.

 


 

The replacement GameThread;

package com.tgt.bitshift;

import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;

/**
 * Created by SCOTT on 27/04/2015.
 */
public class GameThread extends Thread {

    private static final String TAG = GameThread.class.getSimpleName();

    //---

    // Surface holder that can access the physical surface
    private final SurfaceHolder surfaceHolder;
    // The actual view that handles inputs
    // and draws to the surface
    private final GameSurface gameSurface;

    //---

    // flag to hold game state
    private static boolean running;
    public void setRunning(boolean running) {
        GameThread.running = running;
    }

    //---

    private static Canvas canvas = null;

    //---

    public static final boolean useDeltaTime = true;
    public static double deltaTime = 1.0;

    //---

    public final static int TARGET_FPS = 60;
    private final static long OPTIMAL_TIME = 1000000000 / TARGET_FPS;

    //---

    private static long lastLoopTime = System.nanoTime();
    private static long now = System.nanoTime();
    private static long updateLength = now - lastLoopTime;
    private static double delta = updateLength / ((double)OPTIMAL_TIME);

    //---
    //---

    public GameThread(GameSurface inGameSurface) {
        super();
        this.surfaceHolder = inGameSurface.getHolder();
        this.gameSurface = inGameSurface;
    }

    public static void Reset(){
        deltaTime = 1;
        averageCounter = 0;
        sum = 0;
    }

    //---
    //---

    //Variable TimeStep - http://www.java-gaming.org/index.php?topic=24220.0
    @Override
    public void run() {
        Log.d(TAG, "Starting game loop");

        Reset();

        // keep looping round til the game ends
        while (running)
        {
            // work out how long its been since the last update, this
            // will be used to calculate how far the entities should
            // move this loop
            now = System.nanoTime();
            updateLength = now - lastLoopTime;
            lastLoopTime = now;

            if(useDeltaTime) {
                delta = updateLength / ((double)OPTIMAL_TIME);
                //deltaTime = delta;
                CalculateAverage(delta);
            }

            // update the game logic
            Update();

            // draw everyting
            //if(delta <= 1)
            Draw();

            // we want each frame to take 10 milliseconds, to do this
            // we've recorded when we started the frame. We add 10 milliseconds
            // to this and then factor in the current time to give
            // us our final value to wait for
            // remember this is in ms, whereas our lastLoopTime etc. vars are in ns.
            //try{Thread.sleep( (lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000 );}catch(Exception e){};
            try{surfaceHolder.wait( (lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000 );}catch(Exception ignored){}
            //ToastTimed(( (lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000 ));
            //do{}while(!wait);         
        }
    }
    
    //---
    //---

    private static double sum = 0;
    private static double averageCounter = 0;
    private static final double averageCount = TARGET_FPS ;
    private void CalculateAverage(double inValue){
        sum += inValue;
        averageCounter++;

        if(averageCounter >= averageCount){
            deltaTime = sum/(averageCounter+1.0);

            averageCounter = 0;
            sum = 0;
        }
    }

    //---
    //---

    private void Update() {
        // update game state
        this.gameSurface.update();
    }

    //---
    //---

    private void Draw() {

        //canvas = null;

        // try locking the canvas for exclusive pixel editing
        // in the surface
        try {

            canvas = this.surfaceHolder.lockCanvas();

            synchronized (surfaceHolder) {

                // render state to the screen
                // draws the canvas on the panel
                this.gameSurface.render(canvas);

            }

        } finally {

            // in case of an exception the surface is not left in
            // an inconsistent state
            if (canvas != null) {
                surfaceHolder.unlockCanvasAndPost(canvas);
            }

        } // end finally
    }

    //---
    //---

}

 


 

This has helped provide a much visually cleaner scrolling effect – At the expense of smoothness – Bit:Shift especially can experience jittering during gameplay – Especially noticeable on the GooglePlay version of the game, hardly ever occurring in the Amazon release – Something that needs to be fixed.

For the next step, I’m intending to give the ‘GameThread‘ a little space (well, maybe a small tweak) – focus instead more on what’s happening around that code – Are achievements, adverts, Google services, etc weighing too heavily on the system’s resources (They certainly need to be tidied and updated) – Are there still changes in the game code I can make to speed things up?

Hopefully one more push should get things in running nicely at last.

Advertisements

Better Off Android – 1.1 Bit:Update

Android-logo

Last year I put together four simple Android games – Bit:Shift, Bit:Run, Bit:Rise & Bit:Fall.

I wrote these game using scratch code – That is code ‘scratched‘ together from any samples, tutorials, etc I could find on the internet – along with a little guesswork on my part to fill in the gaps..

It creates very messy code, which I wouldn’t want to show anyone. But is a surprisingly useful technique for learning quickly & having a product to use at the end of it all.

By and large I’m pretty happy with these games, there’s not much to them, but I think they are fun to play & have all the elements I wanted to try out – online highscores, achievements, ability to purchase & what I hope is an entertaining diversion at its core.

However they are not without their problems – The most obvious of which is scrolling.

Currently scrolling on these games is atrocious – For the most part – with Bit:Rise & Bit:Fall especially it’s not an issue – But with Bit:Run and especially with Bit:Shift it’s very noticable & takes away most of the fun from playing the game.

I need to try and fix this – If I can’t remove the problem entirely then I need to at least try to minimise their impact.

 


 

Since a scroll fix would involve updating all the games code; I figured I’d do a little housecleaning at the same time.

Really I need to tidy the code & do a little restructuring (not that much) – But I’ll save that for the next update. This time I thought I’d do some optimisation – Especially trying to get the garbage collector to run as infrequently as possible.

 


 

I have four games (Bit:Run, Bit:Rise, Bit:Fall & Bit:Shift) – each games has two versions (One version for GooglePlay , the other for the Amazon App Store).
I used Google’s ‘Performance Tips‘ as a primary guide – alongside a few google searches to decide which to focus on.

Settling on six processes;

1. Remove getters & setters
2. Use direct reference with For loop conditions
3. Convert floats to ints
4. convert int to shorts & bytes
5. Change variables to static & static final
6. Object pools

 

Links:
Performance Tips
Coding for performance and avoiding garbage collection in Android

 


 

1. Remove getters & setters

I’ve always been taught to use getters and setter & I’ve been pretty consistent in using them in code. But never been a huge fan – Unless the getters and setters actually do something (filter the values being retrieved or updated) – They didn’t seem to do anything other than provide an extra layer (& extra code) for what was essentially direct access to an objects variables.

For Android – within an class anyway – Google agree; ‘on Android. Virtual method calls are expensive, much more so than instance field lookups. It’s reasonable to follow common object-oriented programming practices and have getters and setters in the public interface, but within a class you should always access fields directly.

 


 

2. Use direct reference with For loop conditions

The title isn’t particularly clear; so I’ll try to explain through example;

I had a lot of for loops that looked like this;

for(int counter = 0; counter<array.length; counter++)

The problem here is ‘counter<array.length‘ – Because JIT will need to reconfirm the array.length every loop it’s unable to optimise performance.

A better solution (one which I have currently implemented) is to assign ‘array.length‘ to an integer before looping;

int arrayLength = array.length;
for(int counter = 0; counter<arrayLength; counter++)

JIT now has a static value to check against; it can optimise the loop’s performance.

However, an even better method (& one which I haven’t yet implemented) would be to use Java’s ‘enhanced for loop syntax’

for (Foo a : array) {

 


 

3. Convert floats to doubles

Google explainss;

As a rule of thumb, floating-point is about 2x slower than integer on Android-powered devices.

In speed terms, there’s no difference between float and double on the more modern hardware. Space-wise, double is 2x larger. As with desktop machines, assuming space isn’t an issue, you should prefer double to float.

Searching around, the vast majority of people seemed to agreed – With the consensus – use doubles by default & floats only when absolutely necessary.

When are floats necessary? – My games had three, rather major areas, where using floats was necessary;

  1. When creating a large number of objects (e.g. particles for explosions) – Explosions create a lot of particles – I don’t need absolute accuracy when drawing them & using floats means they take up far less space in memory.
  2. Matrices- I use basic graphics for the Bit: games – using matrices to manage them. In java Matrix methods have floats as parameters rather than doubles.
  3. Graphics – Beyond the matrix, most of the graphics methods in Java use floats rather than doubles – GPU’s are generally optimised for float processing – some can’t handle doubles at all.

While doubles are preferred, they are not always feasible – especially with game development.

I guess as a rule – If it’s CPU work, use a double, if its a graphics operation, use floats.

 


 

4. convert int to shorts & bytes

I figured, since I’m use doubles, which take up twice the space of floats – I’d try to save some space by replacing integers (32-bit  min value:-231  max value:231-1) for short’s (16-bit min value:-32,768 max value: 32,767) or bytes (8-bit min value:-128 max value: +127).

This would be especially useful in for loops – Most of my four loops count to less than 10 – a byte which has a value between -128 – +127 should do the job nicely.

As it turns out this is bad idea;
Java is optimised for processing integers, but not shorts or bytes.

Byes & shorts will probably be promoted to integers – a process which will take up processing time to perform & will mean no space has been saved

The Java Virtual Machine models stacks and object fields using offsets that are (in effect) multiples of a 32 bit primitive cell size. So when you declare a local variable or object field as (say) a byte, the variable / field will be stored in a 32 bit cell, just like an int.

Which isn’t to suggest that shorts & bytes aren’t useful – Just not for purposes I had in mind.

Links:
In java, is it more efficient to use byte or short instead of int and float instead of double?
Why does the Java API use int instead of short or byte?

 


 

5. Change variables to static & static final

This makes sense when trying to avoid calling garbage collection. The garbage collector is called when enough elements are orphaned or disconnected (e.g. setting an instantiated object to null). Since static objects exist throughout the lifetime of the application there’s not disconnection.

So, although it ensures a larger constant memory footprint, with fewer objects being destroyed/recreated – There are fewer reasons for Java to run garbage collection.

On a method level, Google explains;
If you don’t need to access an object’s fields, make your method static. Invocations will be about 15%-20% faster. It’s also good practice, because you can tell from the method signature that calling the method can’t alter the object’s state.

Optimisation is further increased if you can declare the objects static final since ‘the constants go into static field initializers in the dex file. Code that refers to intVal will use the integer value 42 directly, and accesses to strVal will use a relatively inexpensive “string constant” instruction instead of a field lookup.

Though, for for static final, it’s worth noting the caveat; ‘This optimization applies only to primitive types and String constants, not arbitrary reference types. Still, it’s good practice to declare constants static final whenever possible.

 


 

6. Object pools

Another garbage collection management method. Object pools use the same principle as using static variables.

Wikipedia explains; ‘initialized objects [are] kept ready to use – [in] a “pool” – rather than allocating and destroying them on demand. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object to the pool rather than destroying it; this can be done manually or automatically.

So, for example – You have a game in which X number of bad guys are going to be active at any one time. Which you can’t guarantee how many will be active, you know it will never be more than 32 at any one time.

To manage the bad guys – You may create a List – of type BadGuy – This way you can easily add new BadGuy’s when they are needed – & remove old BadGuy’s once they have been dispatched by the player. It also means you’re not using up any more memory at any one time than you absolutely need to.

This is fine, except, after x number of BadGuy’s have been removed from the list, the garbage collector will run which will take up precious processing time.

To alleviate this, instead of a list – you use an array – of type badGuy – since we know there’s never more than 32 active bad guys we make the array 32 elements in size.

Also adding a boolean to the badGuy object – called, say isActive – This allows the program to determine if the BadGuy is alive (and should be updated/drawn to screen) or dead (& ignored)

Now when a BadGuy is killed, instead of removing them from the list, you set isActive to false.

When looping through the array (for update or drawing) – you check ‘if badGuy[counter].isActive‘ – to ensure out of play bad guys are not processed.

When adding a new bad guys into the game game; you find a cell in the array with a bad guy whose ‘isActive == false‘ – re-Initialise & they are back in play. You can find an empty cell by looping through the array and checking each element, but a better option is to keep an index of available cells

Like static objects, this has the downside of having a consistently larger memory footprint – but lowers garbage collection calls dramatically.

While I don’t have that many elements active at any one time, objects are constantly falling off the bottom of the screen and being added to the top.

Since this happens in a very controlled manner, converting from lists to basic object pools was pretty straight forward.

Links:
Simple object pool

 


 

Thats was about it – There always more I can do; & for these games, there’s a LOT more that needs to be done – but for now, I figure that’s enough procrastination – I need to get to the heart of the problem & actually fix the scroll issue.