How to count the Framerate with which a SurfaceView refreshes? - android

I have a third party framework that shows video on a SurfaceView. I would like to measure the framerate of the video to check if the phone is capable of showing the video fast enough, or if I need to use another solution to show the information to the user.
How can I measure the speed with that a SurfaceView is updated?

If the code is not clear just ask.
LinkedList<Long> times = new LinkedList<Long>(){{
add(System.nanoTime());
}};
#Override
protected void onDraw(Canvas canvas) {
double fps = fps();
// ...
super.onDraw(canvas);
}
private final int MAX_SIZE = 100;
private final double NANOS = 1000000000.0;
/** Calculates and returns frames per second */
private double fps() {
long lastTime = System.nanoTime();
double difference = (lastTime - times.getFirst()) / NANOS;
times.addLast(lastTime);
int size = times.size();
if (size > MAX_SIZE) {
times.removeFirst();
}
return difference > 0 ? times.size() / difference : 0.0;
}

try this!
private long mLastTime = 0;
private int fps = 0, ifps = 0;
#Override
protected void onDraw(Canvas canvas) {
long now = System.currentTimeMillis();
// perform other operations
System.out.println("FPS:" + fps);
ifps++;
if(now > (mLastTime + 1000)) {
mLastTime = now;
fps = ifps;
ifps = 0;
}
}

Related

how to detect whether the android phone is dropping to the ground?

can i computing the velocity with the accelerations from the sensor which type is SENSOR_TYPE_ACCELEROMETER?
or can i get the orientation of the the accelerations from the sensor which type is SENSOR_TYPE_ACCELEROMETER?
i have seen some blogs about checking the value of the vector of accelerations to detect whether the android phone is filling down or not.
but ,the reson may be the android phone is shaking and is not filling.
my code is:
/**
* how often analysising the accelerometer?(Millisecond)
*/
private static final int ANALYSIS_ACCELERATION_INTERVAL = 500;
/**
* how often checking the accelerometer?(Millisecond)
*/
private static final int CHECK_TIME_INTERVAL = 5000;
/**
* when the sMinorMaxAccelerationInOneSecoud[0] is less than
* ACCELERATION_CRITICAL_VALUE[0],and the
* sMinorMaxAccelerationInOneSecoud[1] is more than
* ACCELERATION_CRITICAL_VALUE[1],i think the phone dropped to the
* ground.
*/
private static float ACCELERATION_CRITICAL_VALUE[] = {2, 40};
/**
* during{#link #ANALYSIS_ACCELERATION_INTERVAL},the min accelerometer
* of the phone is sMinorMaxAccelerationInOneSecoud[0],the max
* accelerometer of the phone is saved at
* sMinorMaxAccelerationInOneSecoud[1].
*/
private static float sMinorMaxAccelerationInOneSecoud[] = {0, 0};
/**
* the last time when checking the accelerometer
*/
private static long sLastCheckTime = 0;
/**
* the last time when analysising the accelerometer
*/
private static long sLastAnalysisTime = 0;
/**
* during {#link #CHECK_TIME_INTERVAL},is phone dropped to ground?
*/
private static boolean sIsFallDown = false;
private SensorManager mSensorManager;
public void receiveCondition() {
// a dialog
Intent intent = new Intent();
intent.setClass(GlobalHolder.getApplicationContext(), PhoneDroppedDialog.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
GlobalHolder.getApplicationContext().startActivity(intent);
}
public void start() {
mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION),
SensorManager.SENSOR_DELAY_UI);
}
public void stop() {
mSensorManager.unregisterListener(this);
mSensorManager = null;
sMinorMaxAccelerationInOneSecoud = new float[]{0, 0};
sLastCheckTime = 0;
sLastAnalysisTime = 0;
}
#Override
public void onSensorChanged(SensorEvent event) {
int sensorType = event.sensor.getType();
if (sensorType != Sensor.TYPE_LINEAR_ACCELERATION) {
return;
}
// judge if the sMinorMaxAccelerationInOneSecoud[0] is less than
// ACCELERATION_CRITICAL_VALUE[0],and the
// sMinorMaxAccelerationInOneSecoud[1] is more than
// ACCELERATION_CRITICAL_VALUE[1]
analysisAcceleration(event.values);
long currentTime = System.currentTimeMillis();
long beta = currentTime - sLastCheckTime;
if (Math.abs(beta) < CHECK_TIME_INTERVAL) {
return;
}
// it have been more than one secound since the last check,so we need re-check if the phone have dropped to the ground.
checkIsFallDown();
// reset sLastCheckTime with the current time
sLastCheckTime = currentTime;
}
private void analysisAcceleration(float[] values) {
if (sIsFallDown) {
// if the phone have already dropped to the ground,we return directly.
return;
}
// whether the min sum vector of the accelerations on the x_axis,y_axis,zaxis is less than ACCELERATION_CRITICAL_VALUE[0]
// and the max sum vector of the accelerations on the x_axis,y_axis,zaxis is more than ACCELERATION_CRITICAL_VALUE[1]
if (sLastAnalysisTime == 0) {
sLastAnalysisTime = System.currentTimeMillis();
}
// the sum vector of the accelerations on the x_axis,y_axis,zaxis
float currentAcceleration = 0f;
for (int i = 0; i < values.length; i++) {
currentAcceleration += (values[i] * values[i]);
}
currentAcceleration = (float) Math.sqrt(currentAcceleration);
if (sMinorMaxAccelerationInOneSecoud[0] == 0 || sMinorMaxAccelerationInOneSecoud[0] > currentAcceleration) {
sMinorMaxAccelerationInOneSecoud[0] = currentAcceleration;
}
if (sMinorMaxAccelerationInOneSecoud[1] == 0 || sMinorMaxAccelerationInOneSecoud[1] < currentAcceleration) {
sMinorMaxAccelerationInOneSecoud[1] = currentAcceleration;
}
long currentTime = System.currentTimeMillis();
long delta = currentTime - sLastAnalysisTime;
if (delta < ANALYSIS_ACCELERATION_INTERVAL) {
return;
}
MLog.d(TAG, "during ANALYSIS_ACCELERATION_INTERVAL, the min value is " + sMinorMaxAccelerationInOneSecoud[0] + ", the max value is " + sMinorMaxAccelerationInOneSecoud[1]);
boolean isLessPre = sMinorMaxAccelerationInOneSecoud[0] < ACCELERATION_CRITICAL_VALUE[0];
boolean isMoreNext = sMinorMaxAccelerationInOneSecoud[1] > ACCELERATION_CRITICAL_VALUE[1];
sIsFallDown = isLessPre && isMoreNext;
sLastAnalysisTime = currentTime;
sMinorMaxAccelerationInOneSecoud = new float[]{0, 0};
}
private void checkIsFallDown() {
if (sIsFallDown) {
// the phone have dropped
receiveCondition();
MLog.d(TAG, "call the receiveCondition method.");
sMinorMaxAccelerationInOneSecoud = new float[]{0, 0};
sIsFallDown = false;
}
}
someone has a better idea?thanks!!

How to increase FPS in Android Game?

I am making an Android Game in which i am only getting in and around 20 FPS. This is the Thread Class i am using:
public class MainThread extends Thread
{
private static final String TAG = MainThread.class.getSimpleName();
// 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;
// Stuff for stats */
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// we'll be reading the stats every second
private final static int STAT_INTERVAL = 1000; //ms
// the average will be calculated by storing
// the last n FPSs
private final static int FPS_HISTORY_NR = 10;
// last time the status was stored
private long lastStatusStore = 0;
// the status time counter
private long statusIntervalTimer = 0l;
// number of frames skipped since the game started
private long totalFramesSkipped = 0l;
// number of frames skipped in a store cycle (1 sec)
private long framesSkippedPerStatCycle = 0l;
// number of rendered frames in an interval
private int frameCountPerStatCycle = 0;
private long totalFrameCount = 0l;
// the last FPS values
private double fpsStore[];
// the number of times the stat has been read
private long statsCount = 0;
// the average FPS since the game started
private double averageFps = 0.0;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private PlayView playView;
// flag to hold game state
private boolean running;
public void setRunning(boolean running)
{
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, PlayView playView)
{
super();
this.surfaceHolder = surfaceHolder;
this.playView = playView;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
// initialise timing elements for stat gathering
initTimingElements();
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.playView.update();
// render state to the screen
// draws the canvas on the panel
this.playView.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
this.playView.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
if (framesSkipped > 0)
{
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
}
finally
{
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null)
{
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
private void storeStats()
{
frameCountPerStatCycle++;
totalFrameCount++;
// check the actual time
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calculate the actual frames pers status check interval
double actualFps = (double)(frameCountPerStatCycle / (STAT_INTERVAL / 1000));
//stores the latest fps in the array
fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;
// increase the number of times statistics was calculated
statsCount++;
double totalFps = 0.0;
// sum up the stored fps values
for (int i = 0; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// obtain the average
if (statsCount < FPS_HISTORY_NR) {
// in case of the first 10 triggers
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// saving the number of total frames skipped
totalFramesSkipped += framesSkippedPerStatCycle;
// resetting the counters after a status record (1 sec)
framesSkippedPerStatCycle = 0;
statusIntervalTimer = 0;
frameCountPerStatCycle = 0;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
// Log.d(TAG, "Average FPS:" + df.format(averageFps));
playView.setAvgFps("FPS: " + df.format(averageFps));
}
}
private void initTimingElements()
{
// initialise timing elements
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");
}
}
Despite my best efforts to improve the FPS, it is not improving. All suggestion for optimizing this code are welcome.

Consequences of recycling Bitmaps?

Hi in my application I have an explosion animation that comes up extremely often. when creating an explosion I load 3 images from resources then once the explosion animation is over I recycle those 3 images. I am constantly doing this and have noticed framerate drops. Is their a better way of doing this like static bitmaps or something?
I once made an application, a simple canvas on which bombs exploded.
I used a tiled bitmap composed of each step of the explosion and drew only a part of it, which changes often in order to create the animation effect.
It updates steps automatically following currentTimestamp
So this is the explosion class:
public class ExplosionAnimated {
private static final String TAG = ExplosionAnimated.class.getSimpleName();
private Bitmap mBitmap;
private Rect mSourceRect;
private int mFrameCountX;
private int mFrameCountY;
private int mCurrentFrame;
private long mFrameTicker;
private int mFramePeriod;
private int mSpriteWidth;
private int mSpriteHeight;
private int mX;
private int mY;
private boolean mFinished = false;
public ExplosionAnimated(Bitmap pBitmap, int pX, int pY,
int pFrameCountX, int pFrameCountY, int pFps) {
this.mBitmap = pBitmap;
this.mX = pX;
this.mY = pY;
this.mCurrentFrame = 0;
this.mFrameCountX = pFrameCountX;
this.mFrameCountY = pFrameCountY;
this.mSpriteWidth = pBitmap.getWidth() / pFrameCountX;
this.mSpriteHeight = pBitmap.getHeight() / pFrameCountY;
this.mSourceRect = new Rect(0, 0, this.mSpriteWidth, this.mSpriteHeight);
this.mFramePeriod = 1000 / pFps;
this.mFrameTicker = 0l;
}
public void update(long gameTime) {
if (gameTime > this.mFrameTicker + this.mFramePeriod) {
this.mFrameTicker = gameTime;
this.mCurrentFrame++;
if (this.mCurrentFrame >= this.mFramePeriod) {
this.mCurrentFrame = 0;
this.mFinished = true;
}
}
if (!this.mFinished) {
this.mSourceRect.left = this.mCurrentFrame * this.mSpriteWidth;
this.mSourceRect.right = this.mSourceRect.left + this.mSpriteWidth;
}
}
public void draw(Canvas canvas) {
Rect destRect = new Rect(this.mX, this.mY,
this.mX + this.mSpriteWidth,
this.mY + this.mSpriteHeight);
canvas.drawBitmap(this.mBitmap, this.mSourceRect, destRect, null);
}
public boolean isFinished() {
return this.mFinished;
}
}
These are methods from an object (Bomb.java for exemple) that starts explosion and draw it:
public void explode(Context pContext, Canvas pCanvas) {
this.mState = State.EXPLODING;
this.mExplosion = new ExplosionAnimated(this.mExplosionBitmap,
(int) this.mX, (int) this.mY, 7, 3, 7);
}
public void doDraw(Canvas pCanvas) {
if (this.mState == State.EXPLODING) {
if (this.mExplosion.isFinished()) {
this.mState = State.EXPLODED;
} else {
this.mExplosion.update(System.currentTimeMillis());
this.mExplosion.draw(pCanvas);
}
} else {
pCanvas.drawBitmap(this.mBombBitmap, this.mX, this.mY, null);
}
}
I used a Thread and a SurfaceView to continually draw the bomb (or the explosion), giving just the currentTimestamp to update the explosion.
I hope it helps, and if you need I can show and explain more code

how manage background rendering?

I want to move background with the bob(Android Game Character) moving for that I make a Dynamic object name Background
public static int BACKGROUND_MOVE=0;
public static int BACKGROUND_FALL=1;
public static final float BACKGROUND_MOVE_VELOCITY =3.5f;
public static float BOB_WIDTH =10.0f;
public static float BOB_HEIGHT =15.0f;
public static long startTime = 0;
public int state;
public float stateTime;
public BackGround(float x, float y)
{
super(x, y, BOB_WIDTH, BOB_HEIGHT);
state = BACKGROUND_MOVE;
stateTime = 0;
accel.set(0,-2);
velocity.y=3.5f;//55
}
public void update (float deltaTime)
{
//velocity.add(World.gravity.x, World.gravity.y * deltaTime);
velocity.add(accel.x * deltaTime,accel.y*deltaTime);
position.add(velocity.x * deltaTime, velocity.y * deltaTime);
if (velocity.y > 0 && state==Bob.BOB_STATE_HIT)//BOB_STATE_HIT is bob running //condition
{
if (state != BACKGROUND_MOVE)
{
state = BACKGROUND_MOVE;
stateTime = 0;
}
}
if (velocity.y > 0 && state != Bob.BOB_STATE_HIT)
{
if (state != BACKGROUND_FALL)
{
state = BACKGROUND_FALL;
stateTime = 0;
}
}
// if (velocity.y < 0 && state == BOB_STATE_HIT)
// {
// if (state != BOB_STATE_JUMP) {
// state = BOB_STATE_JUMP;
// stateTime = 0;
// }
//}
//if (position.y < 0) position.x = World.WORLD_WIDTH;
//if (position.x > World.WORLD_WIDTH) position.x = 0;
stateTime += deltaTime;
}
public void move()
{
if(state==BACKGROUND_MOVE)
{
startTime=System.nanoTime()/1000000000;
// state = BACKGROUND_MOVE;
velocity.y = BACKGROUND_MOVE_VELOCITY;
stateTime = 0;
}
}
similar to bob I amke object in World class and make an ArrayList Add Backgroung into that arraylist and at time of drawing get from arraylist and draw it...but no any effect show simply screen show and cross the rangeand red screen shown...please anyone help..
/*---------------background cam------*/
this.bcam = new OrthographicCamera(FRUSTUM_WIDTH,FRUSTUM_HEIGHT);
this.bcam.position.set(FRUSTUM_WIDTH,FRUSTUM_HEIGHT, 0);
this.batch = batch;
public void renderBackground()
{
GLCommon gl = Gdx.gl;
gl.glClearColor(1, 0, 0, 1);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
bcam.update();
batch.setProjectionMatrix(bcam.combined);
batch.disableBlending();
batch.begin();
if (world.objbackground.state ==BackGround.BACKGROUND_MOVE)
batch.draw(Assets.mainbackgroundRegion, cam.position.x - FRUSTUM_WIDTH / 2, cam.position.y - FRUSTUM_HEIGHT / 2, FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
// else
// {
// batch.draw(Assets.touchbackgroundRegion, cam.position.x -
// FRUSTUM_WIDTH / 2, cam.position.y - FRUSTUM_HEIGHT / 2,
// FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
// if (elapsed == 5)
// changebackground = 0;
// }
batch.end();
}
Either you want to move the background with Bob (1) or you want to have a fixed background (2):
(1): Set background's position to Bob's (probably with an offset, you can do that in the update function of the world), render using batch.draw(......, background.position.x, background.position.y).
(2): Throw away your complicated update methods in the BackGround object and use the render method as you do now.
But honestly, your intentions and explanations are not very clear, try to improve on that!

How to make sprites in Android

I'm developing a game which includes sprites.
currently I'm loading a bitmap and using Rectangle to get the right frame from the bitmap.
the problem is loading bitmaps takes too much memory and I wont be able to load several sprites at the same time.
what are my alternatives for doing sprite in Android?
Try this one:
import org.cocos2d.layers.CCLayer;
public class GameLayer extends CCLayer{
CCSprite mSprite;
protected GameLayer() {
super();
CGSize winSize = CCDirector.sharedDirector().winSize();
mSprite = new CCSprite("image.png");
mSprite.setPosition(CGPoint.ccp(mSprite.getContentSize().width/2.0f, mSprite.getContentSize().height/2.0f));
addChild(mSprite);
}
/* It is Sprite class */
import android.graphics.*;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
public class Sprite {
int x;
private int y;
private boolean visibility = true;
private Bitmap sprite;
private int verticalFrame,horizontalFrame;
private int frameWidth,frameHeight;
private int sequence[] = new int[1];
private int maxFrame = 0;
private int currentFrame;
private int color;
private Rect src;
private Rect dest;
private int frameCount;
private Paint p = new Paint();
private int spInitX,spInitY;
private int rotate = 0;
private float currentRotateAngle = 0;
//private Graphics g;
public float velocityY;
public float velocityX;
public int height;
public Sprite(Bitmap sprite){
this.sprite = sprite;
this.frameWidth = sprite.getWidth();
this.frameHeight = sprite.getHeight();
setFrameCount();
}
public Sprite(Bitmap sprite,int frameWidth,int frameHeight){
this.sprite = sprite;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
setFrameCount();
}
public void rotate(float angle){
currentRotateAngle = angle;
}
public void setImage(Bitmap bm,int frameWidth,int frameHeight){
this.sprite = bm;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
}
public Bitmap getBitmap(){
return sprite;
}
public void paint(Canvas canvas){
dest.offsetTo(getX(), getY());
// g.drawImage(sprite, x, y,src.left,src.top,frameWidth,frameHeight);
canvas.drawBitmap(sprite, src, dest, null);
}
public int getMaxFrame(){
return maxFrame;
}
public int getFrameSequenceLength(){
return sequence.length;
}
public void setFrameSequence(int seq[]){
sequence = seq;
}
public void previousFrame(){
if(sequence.length > 1){
if(frameCount > 0){
setFrame(sequence[frameCount]);
frameCount--;
}else{
frameCount = sequence.length - 1;
setFrame(sequence[frameCount]);
}
}else{
setFrame(frameCount);
if(frameCount > 0){
frameCount++;
}else{
frameCount = maxFrame - 1;
}
}
}
public void setPixel(int x,int y){
spInitX = x;
spInitY = y;
}
public void nextFrame(){
if(sequence.length > 1){
if(frameCount < sequence.length){
setFrame(sequence[frameCount]);
frameCount++;
}else{
frameCount = 0;
setFrame(sequence[frameCount]);
}
}else{
setFrame(frameCount);
if(frameCount < maxFrame){
frameCount++;
}else{
frameCount = 0;
}
}
}
public int getFrame(){
return currentFrame;
}
public void setPosition(int x,int y){
this.x = x;
this.y = y;
}
public void setFrameCount(){
verticalFrame = sprite.getHeight() / frameHeight;
horizontalFrame = sprite.getWidth() / frameWidth;
src = new Rect(0,0,frameWidth,frameHeight);
dest = new Rect(0,0,frameWidth,frameHeight);
maxFrame = verticalFrame * horizontalFrame;
}
public void setFrame(int frame){
if(frame < maxFrame){
currentFrame = frame;
}
int hf = currentFrame % horizontalFrame;
int vf = currentFrame / horizontalFrame;
src.left = hf * frameWidth;
src.right = src.left + frameWidth;
src.top = vf * frameHeight;
src.bottom = src.top + frameHeight;
}
public boolean collidesWith(Sprite sp,boolean cl){
int maxHGap = (getWidth() + sp.getWidth())/2;
int maxVGap = (getHeight() + sp.getHeight())/2;
int x = getX() + getWidth()/2;
int y = getY() + getHeight()/2;
int x1 = sp.getX() + sp.getWidth()/2;
int y1 = sp.getY() + sp.getHeight()/2;
if(Math.abs(x - x1) < maxHGap && Math.abs(y - y1) < maxVGap){
return true;
}
return false;
}
public void setVisible(boolean v){
visibility = v;
}
public final boolean isVisible(){
return visibility;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void move(int moveX,int moveY){
setX(getX()+moveX);
setY(getY()+moveY);
//this.y+=y;
//this.x+=x;
}
public final int getWidth(){
return frameWidth;
}
public final int getHeight(){
return frameHeight;
}
public void setEventY(int i) {
// TODO Auto-generated method stub
}
public int getEventY() {
// TODO Auto-generated method stub
return 0;
}
}
/*Create Main Thread Class Also */
import java.text.DecimalFormat;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
boolean isPaused;
private static final String TAG = MainThread.class.getSimpleName();
// 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;
/* Stuff for stats */
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// we'll be reading the stats every second
private final static int STAT_INTERVAL = 1000; // ms
// the average will be calculated by storing
// the last n FPSs
private final static int FPS_HISTORY_NR = 10;
// last time the status was stored
private long lastStatusStore = 0;
// the status time counter
private long statusIntervalTimer = 0l;
// number of frames skipped since the game started
private long totalFramesSkipped = 0l;
// number of frames skipped in a store cycle (1 sec)
private long framesSkippedPerStatCycle = 0l;
// number of rendered frames in an interval
private int frameCountPerStatCycle = 0;
private long totalFrameCount = 0l;
// the last FPS values
private double fpsStore[];
// the number of times the stat has been read
private long statsCount = 0;
// the average FPS since the game started
private double averageFps = 0.0;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private GameView gv;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, GameView gv) {
super();
this.surfaceHolder = surfaceHolder;
this.gv = gv;
}
public MainThread(Setting setting) {
// TODO Auto-generated constructor stub
}
public void setPause(int i) {
synchronized (gv.getHolder()) {
if (i == 0) {
isPaused = false;
}
if (i == 1) {
isPaused = true;
}
}
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
initTimingElements();
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.gv.update();
// render state to the screen
// draws the canvas on the panel
this.gv.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
this.gv.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check
// if in next frame
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
private void storeStats() {
frameCountPerStatCycle++;
totalFrameCount++;
// assuming that the sleep works each call to storeStats
// happens at 1000/FPS so we just add it up
// statusIntervalTimer += FRAME_PERIOD;
// check the actual time
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calculate the actual frames pers status check interval
double actualFps = (double) (frameCountPerStatCycle / (STAT_INTERVAL / 1000));
// stores the latest fps in the array
fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;
// increase the number of times statistics was calculated
statsCount++;
double totalFps = 0.0;
// sum up the stored fps values
for (int i = 0; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// obtain the average
if (statsCount < FPS_HISTORY_NR) {
// in case of the first 10 triggers
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// saving the number of total frames skipped
totalFramesSkipped += framesSkippedPerStatCycle;
// resetting the counters after a status record (1 sec)
framesSkippedPerStatCycle = 0;
statusIntervalTimer = 0;
frameCountPerStatCycle = 0;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
// Log.d(TAG, "Average FPS:" + df.format(averageFps));
gv.setAvgFps("FPS: " + df.format(averageFps));
}
}
private void initTimingElements() {
// initialise timing elements
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");
}
}

Categories

Resources