Android Game Programming : Game Loop and Animation.
In this tutorial we are going to see the "Game Loop" concept and a little about animations techniques.
- Physics update; this is the game data update as for example the x and y position coordinates for a little character (Sprites positions, Score base in time, ...)
- Drawing; this is about drawing the picture you see in the screen. When this method is called repeatedly it gives you the perception of a movie or of an animation.
package com.edu4java.android.killthemall;
import android.graphics.Canvas;
public class GameLoopThread extends Thread {
private GameView view;
private boolean running = false;
public GameLoopThread(GameView
view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
@Override
public void run() {
while (running) {
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
}
The
running field is a flag that makes the game loop stop. Inside the loop
we call the onDraw method as we learned in the last tutorial. In this
case for simplicity we make the update and drawing activities in the
onDraw method. We use synchronise to avoid some other thread to make
conflict when we are drawing.
In
the SurfaceView we just add an int x field to maintain the x coordinate
to draw the image in the onDraw method. In addition in the onDraw
method we increment x position if it hasn't reached the right border.
package com.edu4java.android.killthemall;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GameView extends SurfaceView
{
private Bitmap bmp;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private int x = 0;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e) {
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
if (x < getWidth() - bmp.getWidth()) {
x++;
}
canvas.drawBitmap(bmp, x, 10, null);
}
}
The animated image below shows us the result. If you watch carefully you can notice that the animation speed is not constant. This is because when the game loop gets more CPU time the animation is faster. We can fix this defining how many FPS , "frames per second" we want in our application.
We
limited the drawing to 10 FPS that is 100 ms (millisecond). We use the
sleep method for the remaining time to get the 100 ms. If the loop
takes more than 100 ms we sleep 10 ms anyway to avoid our application to
be too much CPU demanding.
package com.edu4java.android.killthemall;
import android.graphics.Canvas;
public class GameLoopThread extends Thread {
static final long FPS = 10;
private GameView view;
private boolean running = false;
public GameLoopThread(GameView
view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
@Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch
(Exception e) {}
}
}
}
package com.edu4java.android.killthemall;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GameView extends SurfaceView
{
private Bitmap bmp;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private int x = 0;
private int xSpeed = 1;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e) {
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
}
@Override
protected void onDraw(Canvas canvas) {
if (x == getWidth() - bmp.getWidth()) {
xSpeed = -1;
}
if (x == 0) {
xSpeed = 1;
}
x = x + xSpeed;
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bmp, x , 10, null);
}
}
Comments
Post a Comment