Programmer XR Just someone who explains Android code!

10Mar/1111

Android tutorial: How to make a basic game loop and FPS counter

Android tutorial: Making a game loopThis Android tutorial is all about the basics of Android game development.

You will learn a few things in the tutorial:

  • Create a basic game loop
  • Display the FPS counter (frames per second) on your canvas

In Android we can paint on a canvas. To make sure we do not lock the screen we need to run the operations in a thread. our basic game consists of 4 important files

  1. main.xml
  2. MainActivity
  3. AnimationView
  4. AnimationThread

Lets start off simple:

main.xml

The only thing thats in here is our AnimationView! Pretty simple aint it ?

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">        

        <com.pxr.basegame.AnimationView      
            android:id="@+id/aview"      
            android:layout_width="match_parent"      
            android:layout_height="match_parent"/>
</FrameLayout>

MainActivity

This is the MainActivity:

package com.pxr.basegame;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Only thing it does is loading the main.xml. Well... this is so simple :-) I guess the real fun starts after the break!

AnimationView & AnimationThread

This is where the action happens! The AnimationView is the same View that we saw back in main.xml. Inside the AnimationView is an AnimationThread. This thread is responsible for holding our game loop and drawing the FPS counter to the Canvas. I'll just paste it in so you can see it for yourself. ( Code has a lot of comments to make it easier to understand )

package com.pxr.basegame;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

class AnimationView extends SurfaceView implements SurfaceHolder.Callback {
    class AnimationThread extends Thread {

    	/** Are we running ? */
    	private boolean mRun;

        /** Used to figure out elapsed time between frames */
        private long mLastTime;      

        /** Variables for the counter */
        private int frameSamplesCollected = 0;
        private int frameSampleTime = 0;
        private int fps = 0;

        /** Handle to the surface manager object we interact with */
        private SurfaceHolder mSurfaceHolder;

        /** How to display the text */
        private Paint textPaint;

        public AnimationThread(SurfaceHolder surfaceHolder) {
            mSurfaceHolder = surfaceHolder;

            /** Initiate the text painter */
            textPaint = new Paint();
            textPaint.setARGB(255,255,255,255);
            textPaint.setTextSize(32);
        }

        /**
         * The actual game loop!
         */
        @Override
        public void run() {
            while (mRun) {
                Canvas c = null;
                try {
                    c = mSurfaceHolder.lockCanvas(null);
                    synchronized (mSurfaceHolder) {
                    	updatePhysics();
                        doDraw(c);
                    }
                }finally {
                    if (c != null) {
                        mSurfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }

        /**
         * Figures the gamestate based on the passage of
         * realtime. Called at the start of draw().
         * Only calculates the FPS for now.
         */
        private void updatePhysics() {
            long now = System.currentTimeMillis();

            if (mLastTime != 0) {

            	//Time difference between now and last time we were here
        		int time = (int) (now - mLastTime);
        		frameSampleTime += time;
        		frameSamplesCollected++;

        		//After 10 frames
        		if (frameSamplesCollected == 10) {

        			//Update the fps variable
	        		fps = (int) (10000 / frameSampleTime);

	        		//Reset the sampletime + frames collected
	        		frameSampleTime = 0;
	        		frameSamplesCollected = 0;
        		}
        	}

            mLastTime = now;
        }

        /**
         * Draws to the provided Canvas.
         */
        private void doDraw(Canvas canvas) {

            // Draw the background color. Operations on the Canvas accumulate
            // so this is like clearing the screen. In a real game you can
        	// put in a background image of course
        	canvas.drawColor(Color.BLACK);

        	//Draw fps center screen
        	canvas.drawText(fps + " fps", getWidth() / 2, getHeight() / 2, textPaint);

        	canvas.restore();
        }

        /**
         * So we can stop/pauze the game loop
         */
        public void setRunning(boolean b) {
            mRun = b;
        }      

    }

    /** The thread that actually draws the animation */
    private AnimationThread thread;

    public AnimationView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // register our interest in hearing about changes to our surface
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);

        // create thread only; it's started in surfaceCreated()
        thread = new AnimationThread(holder);

    }

    /**
     * Obligatory method that belong to the:implements SurfaceHolder.Callback
     */

    /**
     * Callback invoked when the surface dimensions change. 
     */
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    /**
     * Callback invoked when the Surface has been created and is ready to be
     * used.
     */
    public void surfaceCreated(SurfaceHolder holder) {
        thread.setRunning(true);
        thread.start();
    }

    /**
     * Callback invoked when the Surface has been destroyed and must no longer
     * be touched. WARNING: after this method returns, the Surface/Canvas must
     * never be touched again!
     */
    public void surfaceDestroyed(SurfaceHolder holder) {
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        thread.setRunning(false);
        while (retry) {
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }
    }
}

Well that it. Basic android game development 101 :-)

I have uploaded a working Eclipse project so you can play around with it.
Download it here: Eclipse project - basic game loop

Questions ? Leave a comment

Want more ? Subscribe to my RSS feed and stay updated!

No related posts.

Comments (11) Trackbacks (3)
  1. Thank you very much for your tutorial and especially including the error-free source code! This was very helpful :)

  2. Hi P-xr: Your code runs as advertised. But when I created a new Android project and typed your scripts in, it didn’t work. I am sure that the .java files and main.xml and the manifest are identical in my version, except that I have used different project and class names (and changed the code to match), but it doesn’t work. The debugger says “source not found”. All my paths are set correctly … can you explain what I am doing wrong, or not doing right?

  3. Mike I think your problem lies in the main.xml. You have to change the following lines

    <com.pxr.gamebase.AnimationView
          android:id=”@+id/aview”
          android:layout_width=”match_parent”
          android:layout_height=”match_parent”/>

    The first line points to the package. If you copied it from my project you probably have to change it to reflect your own package :-)

    I hope that helped a bit.

  4. Hi Mark: I did realise that I needed to edit that line but it had a typo,so that was the problem! Thanks for your help. Now, back to the tutorials …

  5. thanks for your article
    i am having this error at my xml side

    Multiple annotations found at this line:
    - error: Error: String types not allowed (at ‘layout_width’ with value
    ‘match_parent’).
    - error: Error: String types not allowed (at ‘layout_height’ with value
    ‘match_parent’).

    I have exactly the same main.xml as of your(package name is my own).
    if you could please help

  6. Have you tried cleaning your project?

  7. Worked like a charm. Plug and play :)
    Thanks!

  8. Hello. I have never programmed before, so im a newbie. Where do i find a program to do this in?

  9. @Dexter Search with reading up on this: http://p-xr.com/android-development-101-preparing-your-environment/ :-) i hope that will get you started!

  10. Hi, thanks for the tutorial is very helpful

    could you guide me to create a scoreboard to display a win/lose this game


Leave a comment

(required)