Android Games

Android Game Development – The Game Loop

The game loop is the heartbeat of every game. We used a very rudimentary one so far (you can find it here) without any control over how fast or slow we update our game state and which frames to render.

To recapitulate, the most rudimentary game loop is a while loop that keeps executing some instructions until we signal it to finish, usually by setting a variable called running to false

boolean running = true;
while (!running)
{
   updateGameState();
   displayGameState();
}

The above code runs blindly without a care for timing and resources. If you have a fast device then it will run very fast and if you have a slow one it will run slower.

The updateGameState() updates the state of every object in the game and the displayGameState() renders the objects into an image which is displayed onto the screen.

There are two things we should consider here: FPS and UPS.

FPSFrames per Second – the number of times displayGameState() is being called per second.
UPSUpdate per Second – the number of times updateGameState() is being called per second.

Ideally the update and render methods will be called the same number of times per second (preferably not less than 20-25 times per second). 25 FPS is usually enough on a phone so us humans won’t notice the animation being sluggish.

For example if we target 25 FPS then it means we have to call the displayGameState() method every 40ms (1000 / 25 = 40ms, 1000ms = 1s). We need to bear in mind that updateGameState() is also called before the display method and for us to reach 25 FPS, we have to make sure that the update – display sequence executes in exactly 40ms. If it takes less than 40ms, then we have a higher FPS. If it takes more than that, then we have a slower running game.

Let’s see some examples to understand the FPS better.

The following diagram shows exactly 1 FPS. It takes the updaterender cycle exactly one second to execute. This means that you will see the image on the screen change once every second.

1 Frame per Second

The following diagram shows 10FPS. An updaterender cycle takes 100ms. This means every tenth of a second the image changes.

10 FPS

But the above scenario means that the updaterender cycle executes in 1/10 of a second EVERY time. That is an assumption and we can’t control the actual times on cycle executes, or can we? What happens if we have 200 enemies and every enemy is shooting at us? We need to update the state of each enemy and the states of their bullets and check for collisions in one single update. It’s different when we have just 2 enemies. The times will clearly differ. The same applies to the render method. Rendering 200 firing droids will clearly take more time than rendering only 2.

So what are the scenarios? We could have an update-render cycle that finishes in less than 100ms (1/10 of a second), finishes in exactly 100ms or finishes in more than that. On a powerful hardware it will be faster than on a weaker one. Let’s see the diagrams.

The cycle finishes before the desired timeframe so we have a small amount of free time before running the next cycle.

Frame with time to spare

The following diagram shows a cycle which falls behind. That means that the time it takes for a update-render cycle to finish is greater than the desired one. If it takes 12ms that means we are 2ms behind (still considering the 10FPS). This can mount up and every cycle we loose time and the game will run slowly.

Overdue Frame

The first situation is the desired one. This gives us some free time to do something before we kick off the next cycle. We don’t need to do anything so we just tell the game loop to go to sleep for the remaining time period and wake up when the next cycle is due. If we won’t do this the game will run quicker than intended. By introducing the sleep time we achieved constant frame rate.

The second situation (I skipped the ideal one as it almost never happens) when the loop is behind, requires a different approach.

To achieve constant speed in a game we need to update the state of our objects when required. Imagine a droid approaching you at a constant speed. You know if it travelled half the screen in one second, so it will take another second to reach the other side of the screen. To calculate the position accurately we need to know either the time delta since the last postion, and the current speed of the droid, or we update the position (status) of the droid at constant intervals. I will choose the second one as playing with deltas in the game update can be tricky. To achieve a constant game speed we will have to skip displaying frames. Game speed is NOT FPS!

Examine the following diagram. It is a scenario in which the updaterender cycle takes longer than the desired time so we have to catch up. To do that we will skip the rendering of this frame and will do another update so the game speed won’t be affected. We’ll do a normal cycle in the next frame with even some time to give to the CPU to rest.

Constant Game Speed with Variable FPS

The above scenario has many variations. You can imagine the game update taking more than one full frame. In this case we can do nothing to keep the game speed constant and the game will run slower. We might have to skip multiple renderings to keep the speed constant but we have to make sure that we set the maximum number of frames allowed to be skipped because it can take quite a few updates to catch up and in case we skipped 15 frames that means we lost a lot from the game and it will just be unplayable.

The MainThread.java‘s run() looks like this:

// desired fps
private final static int 	MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int	MAX_FRAME_SKIPS = 5;
// the frame period
private final static int	FRAME_PERIOD = 1000 / MAX_FPS;	

@Override
public void run() {
	Canvas canvas;
	Log.d(TAG, "Starting game loop");

	long beginTime;		// the time when the cycle begun
	long timeDiff;		// the time it took for the cycle to execute
	int sleepTime;		// ms to sleep (<0 if we're behind)
	int framesSkipped;	// number of frames being skipped 

	sleepTime = 0;

	while (running) {
		canvas = null;
		// try locking the canvas for exclusive pixel editing
		// in the surface
		try {
			canvas = this.surfaceHolder.lockCanvas();
			synchronized (surfaceHolder) {
				beginTime = System.currentTimeMillis();
				framesSkipped = 0;	// resetting the frames skipped
				// update game state
				this.gamePanel.update();
				// render state to the screen
				// draws the canvas on the panel
				this.gamePanel.render(canvas);
				// calculate how long did the cycle take
				timeDiff = System.currentTimeMillis() - beginTime;
				// calculate sleep time
				sleepTime = (int)(FRAME_PERIOD - timeDiff);

				if (sleepTime > 0) {
					// if sleepTime > 0 we're OK
					try {
						// send the thread to sleep for a short period
						// very useful for battery saving
						Thread.sleep(sleepTime);
					} catch (InterruptedException e) {}
				}

				while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
					// we need to catch up
					// update without rendering
					this.gamePanel.update();
					// add frame period to check if in next frame
					sleepTime += FRAME_PERIOD;
					framesSkipped++;
				}
			}
		} finally {
			// in case of an exception the surface is not left in
			// an inconsistent state
			if (canvas != null) {
				surfaceHolder.unlockCanvasAndPost(canvas);
			}
		}	// end finally
	}
}

Examine the above code very carefully as it implements the logic behind the diagram. You can find the full code in the downloadable project.

There is another approach which I like. It is constant game speed with maximum number of frames per second. It uses interpolation to draw the state and it occurs on fast hardwares when there is time left for another rendering before the next game update. This can enhance the visuals of a game as it enables smoother animation but because we use mobile devices, giving the CPU some rest will save the battery quite a lot.

There is an awesome article on game loops here. I personally understood the game loops reading this article so I highly recommend it. The best I could find.

Note that I also modified the default values in the Speed.java class. The speed is measured in units/second. Because we are setting our desired FPS to 50 that means that the speed will increase by 50*speed.value every update. To have the speed of let’s say 40 pixels/second you will need to set the speed delta for every tick to 2 (40 / (1000 / 50) = 2). In other words you need the droid to advance 2 pixels every game update (when you have 50 game updates per second) to cover 40 pixels per second.

Download the code here and play with it.

Examine it and you have a constant game speed with variable frame rate.

Reference: The Game Loop from our JCG partner Tamas Jano from “Against The Grain” blog.

Do not forget to check out our new Android Game ArkDroid (screenshots below). You feedback will be more than helpful!
Related Articles:
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

11 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Liam Goodacre
Liam Goodacre
12 years ago

I think there is an error in the first code snippet:
while ( !running )

Shouldn’t that be:
while ( running )

nande
12 years ago

Why do you put all your update code inside the synchronized block?
I’ve put only the render part in the synch block and it runs faster

Ian AW Patullo
12 years ago

Really enjoying this series of articles.  Thanks for writing them.  Two comments:
1) At two pixels per update, 50 updates a second, you get 100 pixels a second.
2) Can you explain more about why we add FRAME_PERIOD to sleepTime during the catchup loop, rather than using another time delta?

Thanks

Carlos Alvarez
Carlos Alvarez
12 years ago

How to implement this code on a GLSurfaceView ? In such case, we cannot include rendering in the thread loop.

david hamp-gonsalves
david hamp-gonsalves
12 years ago

In the downloadable src, I think you missed the call to stop the thread in the surface destroyed logic, which causes the app to hang
    thread.setRunning(false);

Ilya_Chu
Ilya_Chu
11 years ago

hm.. i think code is wrong: beginTimes (times when cycle begins) are red dotted lines on diagram, but here i see
beginTime = System.currentTimeMillis() in the beginning of cycle instead of beginTime += FRAME_PERIOD  near both of this.gamePanel.update()

lukatiger
lukatiger
10 years ago

On my device when i implement this code, my droid doesn’t move so smoothly like i had without this code… FPS is 47 so i thought it should move smoothly but it doesn’t… Maybe it’d be better to use the other method like: (currentTime – lastUpdateTime) * speed?

nguyen tuyen
nguyen tuyen
9 years ago

hi. thank you very much.

Ricky
9 years ago

I don’t think sleeping the thread is a good idea, there is always something you can be doing.

synchronized (surfaceHolder) {
startTime = System.currentTimeMillis();

if(timeElapsed > FRAME_PERIOD) {
timeElapsed -= FRAME_PERIOD;
gameView.draw(canvas);
}
gameView.update();

timeElapsed += System.currentTimeMillis() – startTime;
}

Devin
Devin
8 years ago

Following along with the code, I run into a problem when I press the back button on my device. I get this error: “java.lang.NullPointerException: Attempt to invoke virtual method ‘void android.graphics.Canvas.drawColor(int)’ on a null object reference at dev.app.GameView.renderStaticGUI(GameView.java:76) at dev.app.GameView.render(GameView.java:68) at dev.app.towerdefense.ThreadGame.run(ThreadGame.java:47)” What I don’t understand is that if I am pressing the back button while in the game activity, wouldn’t the surfaceView’s surfaceDestroyed() method be called, which will hopefully close down the game thread safely, and eject me back to the main activity? If so then why does the thread run after I press the back button? Any help… Read more »

Carmine
Carmine
6 years ago
Reply to  Devin

For prevent the null pointer when back button is pressed MainThread.running=false;

Back to top button