Sviluppare un Gioco per Android – Lezione 7: Misurare il numero di FPS Android Blog Italia.

// Statistiche */
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// Leggeremo le statistiche ogni secondo
private final static int STAT_INTERVAL = 1000; //ms
// la media sarà calcolata memorizzando
// l'ultimo valore di FPS
private final static int FPS_HISTORY_NR = 10;
// L'ultima volta che lo stato è stato memorizzato
private long lastStatusStore = 0;
// contatore dello stato
private long statusIntervalTimer = 0l;
// numero di fram saltati da quando il gioco è iniziato
private long totalFramesSkipped = 0l;
// numero di frame saltati in un ciclo di memorizzazione (1 sec)
private long framesSkippedPerStatCycle = 0l;
// numero di frame visualizzati in un intervallo
private int frameCountPerStatCycle = 0;
private long totalFrameCount = 0l;
// gli ultimi valori FPS
private double fpsStore[];
// numero di volte che le statistiche sono state lette
private long statsCount = 0;
// media FPS da quando il gioco è iniziato
private double averageFps = 0.0;
A questo punto, ci dovremmo chiedere come modificare la classe MainThread.java affinché sia possibile visualizzare le statistiche da noi desiderate. Per farlo, adotteremo principalmente due metodi e modificheremo leggermente il metodo run(). Gli altri due metodi che utilizzeremo saranno rispettivamente storeStats() per memorizzare i dati e initTimingElements() per inizializzare gli elementi utili al timer per le statistiche. Di seguito potete dare un’occhiata ai tre metodi:
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
// inizializza gli elementi utili ai timer per le statistiche
initTimingElements();
long beginTime; // il tempo quando inizia il ciclo
long timeDiff; // tempo impiegato per il ciclo
int sleepTime; // ms per lo stato di sleep (può essere <0)
int framesSkipped; // numero di frame saltati
sleepTime = 0;
while (running) {
canvas=null;
//Proviamo a bloccare la "tela" per la modifica dei pixel sulla superficie
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resettiamo i frame saltati
// Aggiornamento dello stato di gioco
this.gamePanel.update();
// disegna la "tela" (canvas) nel pannello
this.gamePanel.onDraw(canvas);
// Calcoliamo quanto tempo prende il ciclo
timeDiff = System.currentTimeMillis() - beginTime;
// Calcoliamo sleepTime
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// Caso ottimale se > 0
try {
// mandiamo il thread a dormire per un breve periodo
// molto utile per risparmiare batteria
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 & framesSkipped < MAX_FRAME_SKIPS) {
// Abbiamo bisogno di recuperare
this.gamePanel.update(); // aggiornamento senza rendering
sleepTime += FRAME_PERIOD; // aggiungere frame period per il controllo del frame successivo
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
// per le statistiche
framesSkippedPerStatCycle += framesSkipped;
// memorizza le statistiche
storeStats();
}
} finally {
// Se scatta l'eccezione la superficie non viene lasciata
// in uno stato incoerente
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} //fine finally
}
}
private void storeStats() {
frameCountPerStatCycle++;
totalFrameCount++;
// controlla il tempo attuale
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calcola i frames attuali per ogni intervallo
double actualFps = (double)(frameCountPerStatCycle / (STAT_INTERVAL / 1000));
//memorizza l'ultimo valore FPS nell'array
fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;
// incrementa il numero di volte che le stat vengono calcolate
statsCount++;
double totalFps = 0.0;
// somma i valore memotizzati nell'array
for (int i = 0; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// otteniamo la media
if (statsCount < FPS_HISTORY_NR) {
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// salviamo il numero totale dei frame saltati
totalFramesSkipped += framesSkippedPerStatCycle;
// resettiamo i contatori dopo la memorizzazione di uno stato (1 sec)
framesSkippedPerStatCycle = 0;
statusIntervalTimer = 0;
frameCountPerStatCycle = 0;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
Log.d(TAG, "Average FPS:" + df.format(averageFps));
gamePanel.setAvgFps("FPS: " + df.format(averageFps));
}
}
private void initTimingElements() {
// inizializziamo gli elementi per il timer
fpsStore = new double[FPS_HISTORY_NR];
for (int i = 0; i < FPS_HISTORY_NR; i++) {
fpsStore[i] = 0.0;
}
Log.d(TAG + ".initTimingElements()", "Timing elements for stats initialised");
}
La funzione non è molto difficile da capire. Essa si basa sul conteggio dei fotogrammi e sulla relativa memorizzazione nell’array fpsStore[]. Ad ogni intervallo di 1 secondo, viene chiamato storeStats(). Se l’intervallo di un secondo non viene raggiunto, aggiunge semplicemente il numero dei fotogrammi al conteggio attuale, altrimenti prende il numero di fotogrammi visualizzati e li aggiunge all’array, dopodiché azzera i contatori. La media viene calcolata sui valori memorizzati negli ultimi dieci secondi.
A questo punto, l’ultimo passo è implementare i metodi setAvgFps e displayFps per la visualizzazione della media sul display in alto a destra. Di seguito potete dare un’occhiata alle modifiche da apportare alla classe MainGamePanel.java:
private String avgFps:
protected void onDraw(Canvas canvas) {
//Riempiamo la "tela" di nero
canvas.drawColor(Color.BLACK);
droid.draw(canvas);
displayFps(canvas, avgFps);
}
public void setAvgFps(String avgFps) {
this.avgFps = avgFps;
}
private void displayFps(Canvas canvas, String fps) {
if (canvas != null & fps != null) {
Paint paint = new Paint();
paint.setARGB(255, 255, 255, 255);
canvas.drawText(fps, this.getWidth() - 50, 20, paint);
}
}
Una volta effettuate le modifiche in maniera corretta, potete avviare l’applicazione e vi dovreste trovare dinanzi qualcosa di molto simile al seguente:

A questo punto, non ci rimane che darci appuntamento alla prossima settimana. Nelle prossime lezioni metteremo da parte il progetto Droid per dare risalto ad altre attività relative allo sviluppo di un gioco per Android, come le animazioni Sprite e non solo.
Sviluppare un Gioco per Android – Lezione 7: Misurare il numero di FPS Android Blog Italia.






