GameLoopThread – Geräteunabhängige Geschwindigkeit
Zunächst brauchen wir erst einmal eine globale Variable:
static final long FPS = 20;
Wobei FPS für Frames Per Second steht und wir diesen Wert (statisch) auf 20 setzen.
Damit nun die Geschwindigkeit der Sprites geräteunabhängig ist, brauchen wir erst einmal eine long Variable TPS (Ticks Per Second) die 1000/FPS ist, also wie viele Millisekunden ein Tick zur Verfügung hat. Dann noch zwei long Variablen für die Startzeit und Schlafzeit die wir alle drei in unserer run() Methode oben einfügen:
long TPS = 1000 / FPS;
long startTime, sleepTime;
Danach sorgen wir noch dafür, dass unsere App nicht zu viel Rechenleistung braucht und lassen unsere App für eine Weile schlafen. Um zu bestimmen wie lange wir sie schlafen lassen sollen benötigen wir unsere Variable sleeptime. Die nichts anderes ist, als die Zeit die wir für einen Tick zur Verfügung haben, minus die Zeit die das Smartphone für das Malen gebraucht hat. Sollte diese kleiner als 0 sein, dann soll die App für 10 Millisekunden schlafen. Somit fügen wir folgenden Code in unsere run()- Methode ein:
sleepTime = TPS - (System.currentTimeMillis() - startTime);
startTime = System.currentTimeMillis();
try {
if (sleepTime > 0) sleep(sleepTime);
else sleep(10);
} catch (Exception e) {
}
Damit sich das Bild nun nicht nur von oben nach unten bewegt und dann stehen bleibt, müssen wir unsere Methode onDraw in der GameView Klasse noch modifizieren:
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.DKGRAY);
if(y == getHeight() - bmp.getHeight()){
ySpeed = -1;
}
else if (y==0){
ySpeed = 1;
}
y= y +ySpeed;
canvas.drawBitmap(bmp, 25, y, null);
}
Wenn die y-Position unseres Bildes nun der Höhe des Canvas minus der Höhe des Bildes entspricht, soll die Geschwindigkeit des Bildes umgekehrt werden. Und das Gleiche soll geschehen, wenn das Bild bei der y-Position 0 (also dem oberen Rand) angekommen ist. Damit sich die y-Position nun auch in Abhängigkeit von der Geschwindigkeit bewegt, brauchen wir die Zeile:
y= y +ySpeed;
Die nichts anderes sagt, als dass die neue y-Position die alte y-Position + die y-Geschwindigkeit sein soll.
Am Ende dieses Tutorials sehen die Klassen folgendermaßen aus:
GameLoopThread Class
package com.panjutorials.lazypudding;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
public class GameLoopThread extends Thread {
static final long FPS = 20;
private GameView theView;
private boolean isRunning = false;
public GameLoopThread(GameView theView) {
this.theView = theView;
}
public void setRunning(boolean run) {
isRunning = run;
}
@SuppressLint("WrongCall") @Override
public void run() {
long TPS = 1000 / FPS;
long startTime, sleepTime;
while (isRunning) {
Canvas theCanvas = null;
startTime = System.currentTimeMillis();
try {
theCanvas = theView.getHolder().lockCanvas();
synchronized (theView.getHolder()) {
theView.onDraw(theCanvas);
}
} finally {
if (theCanvas != null) {
theView.getHolder().unlockCanvasAndPost(theCanvas);
}
}
sleepTime = TPS - (System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {
}
}
}
}
GameView Class
package com.panjutorials.lazypudding;
import android.annotation.SuppressLint;
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 SurfaceHolder surfaceHolder;
private Bitmap bmp;
private GameLoopThread theGameLoopThread;
private int y = 0;
private int ySpeed;
@SuppressLint("WrongCall") public GameView(Context context) {
super(context);
theGameLoopThread = new GameLoopThread(this);
surfaceHolder = getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
theGameLoopThread.setRunning(false);
while(retry){
try {
theGameLoopThread.join();
retry=false;
}
catch(InterruptedException e) {
}
}
}
public void surfaceCreated(SurfaceHolder holder) {
theGameLoopThread.setRunning(true);
theGameLoopThread.start();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.DKGRAY);
if(y == getHeight() - bmp.getHeight()){
ySpeed = -1;
}
else if (y==0){
ySpeed = 1;
}
y= y +ySpeed;
canvas.drawBitmap(bmp, 25, y, null);
}
}