Blorb Development Blog

Weblog for upcoming Android game in development, "Blorb"; developed by James Daniello.

Blorb is a top-down box-pusher puzzle game. Features include: multiple 'characters' (in way of a hologram of the main character), over 60+ compelling and challenging levels, and a modestly inexpensive price.

Nov 8

Simple Java Android Game Loop

So, you want to program a steady game? This post is specifically about programming in Java for the Android, but the concepts can be applied to any game in any language. This post explains how to write a gameLoop() that allows a lot of control without using a refreshHandler or without calling the invalidate() method to force the game to draw (which can do so at an inconstant rate). It also explains how to create a steady loop that sleeps for a variable amount of time, taking into consideration the time required to update and render the actual game. There’s a couple of things you need to take into consideration first, so I’ll break it down and show you how I did it.

There’s many ways to go about this. Some of the methods I’ve found have the OS redrawing the screen, or invoking the OS to do so. I find the best way is to create a ‘tick’ (repeated every game loopin) that calls the gameUpdate() and gameRender() methods within a self contained gameLoop(), which then repeats until the game is done. Going this route, we can time how long it takes to update and render and then subtract that from the desired amount of sleep time (time between updates). This makes for a steady game.

Creating an Android game in this manner requires three things:

  1. STEP 1: You need a game engine. I’ll assume you’ve created this already or know how to implement one. I’m only going to show you how to create a consitent loop which draws to the Android’s screen. Your game engine should have two public methods that will be called from within the game loop; an update() method, and a draw(Canvas) method.

  2. STEP 2: A class that extends Android’s SurfaceView. Inheriting from this class will give us all the methods and variables we need to draw to the screen.

  3. STEP 3: A separate class which extends Java’s Thread class. This class will contain the actual game loop. It will be initialized when the SurfaceView object is initialized and loops until the game finishes. At the end of every game loop the Thread will call sleep() for the desired amount of time, minus the time it took to actually update and render the game. This allows our game to be more consistent than if we had just slept for the same amount of time every loop.



First, I’ll explain the SurfaceView class. You’ll want to create a class like this to interact with the cell phone’s screen:

public class GameEngineView extends SurfaceView implements SurfaceHolder.Callback
{
    //used to keep track of time between updates and amount of time to sleep for
    long lastUpdate = 0;
    long sleepTime=0;
    //Game engine
    GameEngine gEngine;

    //objects which house info about the screen
    SurfaceHolder surfaceHolder;
    Context context;
 
    //our Thread class which houses the game loop
    private PaintThread thread;
 
    //initialization code
    void InitView(){
      //initialize our screen holder
      SurfaceHolder holder = getHolder();
      holder.addCallback( this);

      //initialize our game engine
      gEngine = new GameEngine();
      gEngine.Init(context.getResources());
  
      //initialize our Thread class. A call will be made to start it later
      thread = new PaintThread(holder, context, new Handler(), gEngine);
      setFocusable(true);
   }
 
   //class constructors
   public GameEngineView(Context contextS, AttributeSet attrs, int defStyle){
       super(contextS, attrs, defStyle);
       context=contextS;
       InitView();
   }
   public GameEngineView(Context contextS, AttributeSet attrs){
       super(contextS, attrs);
       context=contextS;
       InitView();
   }
 

[Various code to capture key presses, screen changes and screen touch events]

    //these methods are overridden from the SurfaceView super class. They are automatically called 
    when a SurfaceView is created, resumed or suspended.
    @Override 
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {}

    @Override 
    public void surfaceDestroyed(SurfaceHolder arg0) {
        boolean retry = true;
        //code to end gameloop
        thread.state=PaintThread.PAUSED;
        while (retry) {
            try {
                //code to kill Thread
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }

    }

    @Override 
    public void surfaceCreated(SurfaceHolder arg0) {
        if(thread.state==PaintThread.PAUSED){
            //When game is opened again in the Android OS
            thread = new PaintThread(getHolder(), context, new Handler(), gEngine);
            thread.start();
        }else{
            //creating the game Thread for the first time
            thread.start();
        }
    }
}



The SurfaceCreated() and surfaceDestroyed() methods are called when the operating system launches, resumes, or suspends the application. Since the game engine is a separate class it is untouched. However, you may want to make sure that in your Android application .xml file, you only allow a single instance of the application and prevent the system from destroying it when suspended by including the following code in your <activity> tag:

android:launchMode=”singleTask” android:alwaysRetainTaskState=”true”



Now we’ll take a look at the Thread class. Mine is called PaintThread:

public class PaintThread extends Thread{
    private SurfaceHolder mSurfaceHolder;
    private Handler mHandler;
    private Context mContext;
    private Paint mLinePaint;
    private Paint blackPaint;
    GameEngine gEngine;

    //for consistent rendering
    private long sleepTime;
    //amount of time to sleep for (in milliseconds)
    private long delay=70;
    
    //state of game (Running or Paused).
    int state = 1;
    public final static int RUNNING = 1;
    public final static int PAUSED = 2;

    public PaintThread(SurfaceHolder surfaceHolder, Context context, Handler handler, 
                               GameEngine gEngineS) {
            
        //data about the screen
        mSurfaceHolder = surfaceHolder;
        this.mHandler = handler;
        this.mContext = context;

        //standard game painter. Used to draw on the canvas
        mLinePaint = new Paint();
        mLinePaint.setARGB(255, 0, 255, 0);
        //black painter below to clear the screen before the game is rendered
        blackPaint = new Paint();
        blackPaint.setARGB(255, 0, 0, 0);
        //mLinePaint.setAntiAlias(true);

        gEngine=gEngineS;
    }


    //This is the most important part of the code. It is invoked when the call to start() is
    //made from the SurfaceView class. It loops continuously until the game is finished or
    //the application is suspended.
    @Override
    public void run() {

     //UPDATE
     while (state==RUNNING) {
           //time before update
           long beforeTime = System.nanoTime();
           //This is where we update the game engine
           gEngine.Update();

     //DRAW
     Canvas c = null;
     try {
           //lock canvas so nothing else can use it
           c = mSurfaceHolder.lockCanvas(null);
           synchronized (mSurfaceHolder) {
                //clear the screen with the black painter.
                c.drawRect(0, 0, c.getWidth(), c.getHeight(), blackPaint);
                //This is where we draw the game engine.
                gEngine.Draw(c);
         }
     } finally {
         // do this in a finally so that if an exception is thrown
         // during the above, we don't leave the Surface in an
         // inconsistent state
         if (c != null) {
             mSurfaceHolder.unlockCanvasAndPost(c);
         }
     }



     //SLEEP
     //Sleep time. Time required to sleep to keep game consistent
     //This starts with the specified delay time (in milliseconds) then subtracts from that the
     //actual time it took to update and render the game. This allows our game to render smoothly.
     this.sleepTime = delay-((System.nanoTime()-beforeTime)/1000000L);

            try {
                //actual sleep code
                if(sleepTime>0){
                this.sleep(sleepTime);
                }
            } catch (InterruptedException ex) {
                Logger.getLogger(PaintThread.class.getName()).log(Level.SEVERE, null, ex);
            }
     }

    }
}

The code is documented so it should be pretty self explanatory. It took 5+ hours to figure all of this out since I couldn’t find a clear, easy to follow article anywhere that explained how to design a game loop with this much control over timing and rendering. If you have any questions leave a comment at the bottom of the page and I’ll do my best to help you out or make the post clearer.

[The code was adapted and modified from the Lunar Lander example that comes with the Android SDK. Since that code is open source, feel free to use this code without my permission, but also feel free to show me anything cool you’ve designed by using this article. ;) ]

James




Page 1 of 1