Hi Andre,

you should definitely read the "Designing for Performance" document
from:
http://developer.android.com/guide/practices/design/performance.html

I think you might speed up your onDraw() method a little bit this way,
though I think you might not reach more than 30 or 35 FPS.
At first you should eliminate all function calls in your onDraw()
method. I know this is bad code style, but it really is faster. I am
drawing some bitmaps for a project here this way.
Second, you should create local variables for all attributes you use
more than once.
Third and most importantly: Remove unnecessary code. For example, you
call System.currentTimeMillis() relatively often (I think you used
this code for performance measuring, but anyway).

And last but not least: Android comes with a profiler. Try to use
it. :-)


Regards

Marc Reichelt   ||   http://www.marcreichelt.de/

On 18 Jan., 10:16, Andre <andranik.abra...@gmail.com> wrote:
> Hello to all developers!
>
> First I want to say that I have read many articles on this subject in
> this forum and on some external resources(very helpful was Robert
> Green's diary atwww.rbgrn.netandwww.droidnova.com).
>
> However, despite all of this I want to start topic regarding FPS, and
> ask for advice from experienced game developers on Android platform.
>
> My main question is "How to improve FPS when draw on Canvas?"
>
> I have implemented simple 2D arcade game skeleton for testing purpose.
> Now I have ~20 FPS and want to increase this value to 40-50 FPS, if
> this is possible of course. I know about Open GL ES, but so far I am
> interested in Canvas.
>
> In every frame I draw following stuff on the screen:
>   - Canvas.drawColor(Color.BLACK) - to clear the screen
>   - 1 spaceship PNG 24x24 image 1.25 kb
>   - 5 asteroids PNG 64x64 image ~8 kb each
>   - from 1 to 30 bullets PNG 8x8 image 299 b
>   - 4 30x30 Rectangles - to control objects on the screen
>
> After running my app, in logcat I can see following output data:
>   - Average FPS: 20 (Total frames drawn: 1945 in 97 seconds)
>   - Average onDraw: 32 ms (clear canvas: 3, draw game stuff: 26, draw
> controls: 1)
>   - Average updatePhysics: 1 ms
>
> From this output I can assume, that my main problem here is "draw game
> stuff" wich includes:
>   - draw 1 spaceship
>   - draw 5 asteroids
>   - draw from 1 to 30 bullets
>
> Thanks in advance
> Best Regards, Andre
>
> P.S.: sorry for my English
>
> Here is my code:
>
> --------------------------------------GAME
> VIEW--------------------------------------
>
> package com.example.game.asteroids;
>
> import java.util.Random;
>
> import android.content.Context;
> import android.graphics.BitmapFactory;
> import android.graphics.Canvas;
> import android.graphics.Color;
> import android.graphics.Paint;
> import android.graphics.Rect;
> import android.util.Log;
> import android.view.MotionEvent;
> import android.view.SurfaceHolder;
> import android.view.SurfaceView;
>
> public class GameView extends SurfaceView implements
> SurfaceHolder.Callback {
>         private static final String TAG = "Asteroids";
>         private static final float MAX_VELOCITY = 3.0f;
>         private static final int MAX_ASTEROIDS = 5;
>         private static final int MAX_BULLETS = 30;
>         private static final int[] ASTEROID_IMG_RES =
>                 {R.drawable.asteroid1, R.drawable.asteroid2, 
> R.drawable.asteroid3,
> R.drawable.asteroid4, R.drawable.asteroid5};
>         //private static final Bitmap.Config BITMAP_CONFIG =
> Bitmap.Config.ARGB_4444;
>         private GameLoop mGameLoop;
>         private GameEntity mSpaceship;
>         private GameEntity[] mAsteroids;
>         private GameEntity[] mBullets;
>         private int mCurrentBullet;
>         private boolean mRight;
>         private boolean mLeft;
>         private boolean mUp;
>         private boolean mFire;
>         private long mFireTime;
>         private Rect mVelocityControl;
>         private Paint mVelocityControlPaint;
>         private Rect mLeftControl;
>         private Paint mLeftControlPaint;
>         private Rect mRightControl;
>         private Paint mRightControlPaint;
>         private Rect mFireControl;
>         private Paint mFireControlPaint;
>         private Random mRandom;
>
>         private long mStartMillis;
>         private int mFrameCounter;
>         private int mFps;
>         private int mOverallSeconds;
>         private int mOverallFrameCounter;
>         private long mOnDrawTimer;
>         private int mOnDrawCounter;
>         private long mDrawColorTimer;
>         private long mDrawStuffTimer;
>         private long mDrawControlsTimer;
>         private long mUpdateTimer;
>         private int mUpdateCounter;
>
>         public GameView(Context context) {
>                 super(context);
>                 SurfaceHolder holder = getHolder();
>                 holder.addCallback(this);
>                 setFocusable(true);
>                 mGameLoop = new GameLoop(holder, this);
>                 mRandom = new Random();
>                 mRight = false;
>                 mLeft = false;
>                 mUp = false;
>                 mFire = false;
>                 mVelocityControl = new Rect(0, 0, 30, 30);
>                 mVelocityControlPaint = new Paint();
>                 mVelocityControlPaint.setColor(Color.GREEN);
>                 mLeftControl = new Rect(31, 0, 61, 30);
>                 mLeftControlPaint = new Paint();
>                 mLeftControlPaint.setColor(Color.YELLOW);
>                 mRightControl = new Rect(62, 0, 92, 30);
>                 mRightControlPaint = new Paint();
>                 mRightControlPaint.setColor(Color.BLUE);
>                 mFireControl = new Rect(93, 0, 123, 30);
>                 mFireControlPaint = new Paint();
>                 mFireControlPaint.setColor(Color.CYAN);
>
>                 mStartMillis = System.currentTimeMillis();
>         }
>
> //      private Bitmap loadBitmap(Drawable sprite, Bitmap.Config cfg) {
> //              int width = sprite.getIntrinsicWidth();
> //              int height = sprite.getIntrinsicHeight();
> //              Bitmap bitmap = Bitmap.createBitmap(width, height, cfg);
> //              Canvas canvas = new Canvas(bitmap);
> //              sprite.setBounds(0, 0, width, height);
> //              sprite.draw(canvas);
> //              return bitmap;
> //      }
>
>         private void initSpaceship() {
>                 mSpaceship = new 
> GameEntity(BitmapFactory.decodeResource(getResources
> (), R.drawable.spaceship));
>                 //loadBitmap(getResources().getDrawable(R.drawable.spaceship),
> BITMAP_CONFIG));
>                 //Log.d(TAG, "bitmap config: " + 
> bitmap.getConfig().toString());
>                 mSpaceship.setX(getWidth() / 2);
>                 mSpaceship.setY(getHeight() / 2);
>                 mSpaceship.setAlive(true);
>         }
>
>         private void initAsteroids() {
>                 GameEntity[] asteroids = new GameEntity[MAX_ASTEROIDS];
>                 Random rnd = mRandom;
>                 float angle;
>                 for(int i = 0; i < MAX_ASTEROIDS; i++) {
>                         asteroids[i] = new 
> GameEntity(BitmapFactory.decodeResource
> (getResources(), ASTEROID_IMG_RES[rnd.nextInt(5)]));
>                         
> //loadBitmap(getResources().getDrawable(ASTEROID_IMG_RES[rnd.nextInt
> (5)]), BITMAP_CONFIG));
>                         asteroids[i].setRotationVelocity(rnd.nextInt(3) + 1);
>                         asteroids[i].setX(rnd.nextInt(getWidth()) - 20);
>                         asteroids[i].setY(rnd.nextInt(getHeight()) - 20);
>                         asteroids[i].setMoveAngle(rnd.nextInt(360));
>                         angle = asteroids[i].getMoveAngle() - 90;
>                         asteroids[i].setVelocityX(calculateMoveAngleX(angle));
>                         asteroids[i].setVelocityY(calculateMoveAngleY(angle));
>                         asteroids[i].setAlive(true);
>                 }
>                 mAsteroids = asteroids;
>         }
>
>         private void initBullets() {
>                 GameEntity[] bullets = new GameEntity[MAX_BULLETS];
>                 for(int i = 0; i < MAX_BULLETS; i++) {
>                         bullets[i] = new 
> GameEntity(BitmapFactory.decodeResource
> (getResources(), R.drawable.plasmashot));
>                         
> //loadBitmap(getResources().getDrawable(R.drawable.plasmashot),
> BITMAP_CONFIG));
>                 }
>                 mBullets = bullets;
>         }
>
>         @Override
>         protected void onDraw(Canvas canvas) {
>                 long mainStart = System.currentTimeMillis();
>                 mFrameCounter++;
>                 if(System.currentTimeMillis() > mStartMillis + 1000) {
>                         mOverallSeconds++;
>                         mOverallFrameCounter += mFrameCounter;
>                         mStartMillis = System.currentTimeMillis();
>                         mFps = mFrameCounter;
>                         mFrameCounter = 0;
>                         Log.d(TAG, "FPS: " + mFps);
>                 }
>                 long colorStart = System.currentTimeMillis();
>                 canvas.drawColor(Color.BLACK);
>                 long colorFinish = System.currentTimeMillis();
>                 long stuffStart = System.currentTimeMillis();
>                 drawSpaceship(canvas);
>                 drawAsteroids(canvas);
>                 drawBullets(canvas);
>                 long stuffFinish = System.currentTimeMillis();
>                 long controlsStart = System.currentTimeMillis();
>                 canvas.setMatrix(null);
>                 canvas.drawRect(mVelocityControl, mVelocityControlPaint);
>                 canvas.drawRect(mLeftControl, mLeftControlPaint);
>                 canvas.drawRect(mRightControl, mRightControlPaint);
>                 canvas.drawRect(mFireControl, mFireControlPaint);
>                 long controlsFinish = System.currentTimeMillis();
>                 long mainFinish = System.currentTimeMillis();
>                 mOnDrawTimer += mainFinish - mainStart;
>                 mDrawColorTimer += colorFinish - colorStart;
>                 mDrawStuffTimer += stuffFinish - stuffStart;
>                 mDrawControlsTimer += controlsFinish - controlsStart;
>                 mOnDrawCounter++;
>         }
>
>         public void updatePhysics() {
>                 long start = System.currentTimeMillis();
>                 updateSpaceshipPhysics();
>                 updateAsteroidsPhysics();
>                 updateBulletsPhysics();
>                 checkForCollisions();
>                 long finish = System.currentTimeMillis();
>                 mUpdateTimer += finish - start;
>                 mUpdateCounter++;
>         }
>
>         @Override
>         public void surfaceChanged(SurfaceHolder holder, int format, int
> width,
>                         int height) {
>                 // TODO Auto-generated method stub
>         }
>
>         @Override
>         public void surfaceCreated(SurfaceHolder holder) {
>                 initSpaceship();
>                 initAsteroids();
>                 initBullets();
>                 mGameLoop.setRunning(true);
>                 mGameLoop.start();
>         }
>
>         @Override
>         public void surfaceDestroyed(SurfaceHolder holder) {
>                 Log.d(TAG, "Average FPS: " + mOverallFrameCounter / 
> mOverallSeconds
> +
>                                 " (Total frames drawn: " + 
> mOverallFrameCounter + " in " +
> mOverallSeconds + " seconds)");
>                 Log.d(TAG, "Average onDraw: " + mOnDrawTimer / mOnDrawCounter 
> + "
> ms" +
>                                 " (clear canvas: " + mDrawColorTimer / 
> mOnDrawCounter +
>                                 ", draw game stuff: " + mDrawStuffTimer / 
> mOnDrawCounter +
>                                 ", draw controls: " + mDrawControlsTimer / 
> mOnDrawCounter + ")");
>                 Log.d(TAG, "Average updatePhysics: " + mUpdateTimer / 
> mUpdateCounter
> + " ms");
>                 boolean retry = true;
>                 mGameLoop.setRunning(false);
>                 while(retry) {
>                         try {
>                                 mGameLoop.join();
>                                 retry = false;
>                         } catch(InterruptedException ex) {
>                         }
>                 }
>         }
>
>         private void drawSpaceship(Canvas canvas) {
>                 float x = mSpaceship.getX();
>                 float y = mSpaceship.getY();
>                 float angle = mSpaceship.getFaceAngle();
>                 int width = mSpaceship.getWidth();
>                 int height = mSpaceship.getHeight();
>                 //canvas.translate(x, y);
>                 canvas.rotate(angle, x, y);
>                 canvas.drawBitmap(mSpaceship.getBitmap(), x - width / 2, y -
> height / 2, null);
>         }
>
>         private void drawAsteroids(Canvas canvas) {
>                 GameEntity[] asteroids = mAsteroids;
>                 for(int i = 0; i < MAX_ASTEROIDS; i++) {
>                         if(asteroids[i].isAlive()) {
>                                 canvas.setMatrix(null);
>                                 //canvas.translate(asteroids[i].getX(), 
> asteroids[i].getY());
>                                 float x = asteroids[i].getX();
>                                 float y = asteroids[i].getY();
>                                 int width = asteroids[i].getWidth();
>                                 int height = asteroids[i].getHeight();
>                                 canvas.rotate(asteroids[i].getMoveAngle(), x, 
> y);
>                                 canvas.drawBitmap(asteroids[i].getBitmap(), x 
> - width / 2, y -
> height / 2, null);
>                         }
>                 }
>         }
>
>         private void drawBullets(Canvas canvas) {
>                 GameEntity[] bullets = mBullets;
>                 for(int i = 0; i < MAX_BULLETS; i++) {
>                         if(bullets[i].isAlive()) {
>                                 canvas.setMatrix(null);
>                                 //canvas.translate(bullets[i].getX(), 
> bullets[i].getY());
>                                 float x = bullets[i].getX();
>                                 float y = bullets[i].getY();
>                                 int width = bullets[i].getWidth();
>                                 int height = bullets[i].getHeight();
>                                 canvas.rotate(bullets[i].getMoveAngle() + 90, 
> x, y);
>                                 canvas.drawBitmap(bullets[i].getBitmap(), x - 
> width / 2, y -
> height / 2, null);
>                         }
>                 }
>         }
>
>         private void updateSpaceshipPhysics() {
>                 GameEntity spaceship = mSpaceship;
>                 if(mLeft) {
>                         spaceship.incFaceAngle(-5);
>                         if(spaceship.getFaceAngle() < 0) {
>                                 spaceship.setFaceAngle(360 - 5);
>                         }
>                 }
>                 if(mRight) {
>                         spaceship.incFaceAngle(5);
>                         if(spaceship.getFaceAngle() > 360) {
>                                 spaceship.setFaceAngle(5);
>                         }
>                 }
>                 if(mUp) {
>                         spaceship.setMoveAngle(spaceship.getFaceAngle() - 90);
>                         if(Math.abs(spaceship.getVelocityX()) < MAX_VELOCITY) 
> {
>                                 
> spaceship.incVelocityX(calculateMoveAngleX(spaceship.getMoveAngle
> ()) * 0.1f);
>                         }
>                         if(Math.abs(spaceship.getVelocityY()) < MAX_VELOCITY) 
> {
>                                 
> spaceship.incVelocityY(calculateMoveAngleY(spaceship.getMoveAngle
> ()) * 0.1f);
>                         }
>                 }
>                 spaceship.incX(spaceship.getVelocityX());
>                 if(spaceship.getX() < -10) {
>                         spaceship.setX(getWidth() + 10);
>                 } else if(spaceship.getX() > getWidth() + 10) {
>                         spaceship.setX(-10);
>                 }
>                 spaceship.incY(spaceship.getVelocityY());
>                 if(spaceship.getY() < -10) {
>                         spaceship.setY(getHeight() + 10);
>                 } else if(spaceship.getY() > getHeight() + 10) {
>                         spaceship.setY(-10);
>                 }
>                 mSpaceship = spaceship;
>         }
>
>         private void updateAsteroidsPhysics() {
>                 GameEntity[] asteroids = mAsteroids;
>                 for(int i = 0; i < MAX_ASTEROIDS; i++) {
>                         if(asteroids[i].isAlive()) {
>                                 
> asteroids[i].incX(asteroids[i].getVelocityX());
>                                 if(asteroids[i].getX() < -20) {
>                                         asteroids[i].setX(getWidth() + 20);
>                                 } else if(asteroids[i].getX() > getWidth() + 
> 20) {
>                                         asteroids[i].setX(-20);
>                                 }
>                                 
> asteroids[i].incY(asteroids[i].getVelocityY());
>                                 if(asteroids[i].getY() < -20) {
>                                         asteroids[i].setY(getHeight() + 20);
>                                 } else if(asteroids[i].getY() > getHeight() + 
> 20) {
>                                         asteroids[i].setY(-20);
>                                 }
>                                 
> asteroids[i].incMoveAngle(asteroids[i].getRotationVelocity());
>                                 if(asteroids[i].getMoveAngle() < 0) {
>                                         asteroids[i].setMoveAngle(360 - 
> asteroids[i].getRotationVelocity
> ());
>                                 } else if(asteroids[i].getMoveAngle() > 360) {
>                                         
> asteroids[i].setMoveAngle(asteroids[i].getRotationVelocity());
>                                 }
>                         }
>                 }
>                 mAsteroids = asteroids;
>         }
>
>         private void updateBulletsPhysics() {
>                 GameEntity[] bullets = mBullets;
>                 int currentBullet = mCurrentBullet;
>                 if(mFire && (System.currentTimeMillis() > mFireTime + 250)) {
>                         mFireTime = System.currentTimeMillis();
>                         currentBullet++;
>                         if(currentBullet > MAX_BULLETS - 1) {
>                                 currentBullet = 0;
>                         }
>                         bullets[currentBullet].setAlive(true);
>                         bullets[currentBullet].setX(mSpaceship.getX());
>                         bullets[currentBullet].setY(mSpaceship.getY());
>                         
> bullets[currentBullet].setMoveAngle(mSpaceship.getFaceAngle() -
> 90);
>                         float angle = bullets[currentBullet].getMoveAngle();
>                         float spaceshipVelocityX = mSpaceship.getVelocityX();
>                         float spaceshipVelocityY = mSpaceship.getVelocityY();
>                         
> bullets[currentBullet].setVelocityX(spaceshipVelocityX +
> calculateMoveAngleX(angle) * 2);
>                         
> bullets[currentBullet].setVelocityY(spaceshipVelocityY +
> calculateMoveAngleY(angle) * 2);
>                 }
>                 for(int i = 0; i < MAX_BULLETS; i++) {
>                         if(bullets[i].isAlive()) {
>                                 bullets[i].incX(bullets[i].getVelocityX());
>                                 if(bullets[i].getX() < 0 || bullets[i].getX() 
> > getWidth()) {
>                                         bullets[i].setAlive(false);
>                                 }
>                                 bullets[i].incY(bullets[i].getVelocityY());
>                                 if(bullets[i].getY() < 0 || bullets[i].getY() 
> > getHeight()) {
>                                         bullets[i].setAlive(false);
>                                 }
>                         }
>                 }
>                 mBullets = bullets;
>                 mCurrentBullet = currentBullet;
>         }
>
>         private void checkForCollisions() {
>                 GameEntity[] asteroids = mAsteroids;
>                 GameEntity[] bullets = mBullets;
>                 GameEntity spaceship = mSpaceship;
>                 for(int i = 0; i < MAX_ASTEROIDS; i++) {
>                         if(asteroids[i].isAlive()) {
>                                 for(int j = 0; j < MAX_BULLETS; j++) {
>                                         if(bullets[j].isAlive()) {
>                                                 
> if(asteroids[i].getBounds().intersect(bullets[j].getBounds())) {
>                                                         
> bullets[j].setAlive(false);
>                                                         
> asteroids[i].setAlive(false);
>                                                         continue;
>                                                 }
>                                         }
>                                 }
>                                 
> if(asteroids[i].getBounds().intersect(spaceship.getBounds())) {
>                                         asteroids[i].setAlive(false);
>                                         spaceship.setX(getWidth() / 2);
>                                         spaceship.setY(getHeight() / 2);
>                                         spaceship.setFaceAngle(0);
>                                         spaceship.setVelocityX(0);
>                                         spaceship.setVelocityY(0);
>                                         continue;
>                                 }
>                         }
>                 }
>                 mAsteroids = asteroids;
>                 mBullets = bullets;
>                 mSpaceship = spaceship;
>         }
>
>         @Override
>         public boolean onTouchEvent(MotionEvent event) {
>                 int action = event.getAction();
>                 int x = (int)event.getX();
>                 int y = (int)event.getY();
>                 if(action == MotionEvent.ACTION_DOWN) {
>                         if(mVelocityControl.contains(x, y)) {
>                                 mUp = true;
>                         } else if(mLeftControl.contains(x, y)) {
>                                 mLeft = true;
>                         } else if(mRightControl.contains(x, y)) {
>                                 mRight = true;
>                         } else if(mFireControl.contains(x, y)) {
>                                 mFire = true;
>                         }
>                 } else if(action == MotionEvent.ACTION_UP) {
>                         mUp = false;
>                         mLeft = false;
>                         mRight = false;
>                         mFire = false;
>                         mSpaceship.setVelocityX(0.0f);
>                         mSpaceship.setVelocityY(0.0f);
>                 }
>                 return true;
>         }
>
>         private float calculateMoveAngleX(float angle) {
>                 return (float)Math.cos(angle * Math.PI / 180);
>         }
>
>         private float calculateMoveAngleY(float angle) {
>                 return (float)Math.sin(angle * Math.PI / 180);
>         }
>
> }
>
> --------------------------------------GAME
> LOOP--------------------------------------
>
> package com.example.game.asteroids;
>
> import android.graphics.Canvas;
> import android.view.SurfaceHolder;
>
> public class GameLoop extends Thread {
>         private SurfaceHolder mHolder;
>         private GameView mGameView;
>         private boolean mRunning;
>
>         public GameLoop(SurfaceHolder holder, GameView gameView) {
>                 mHolder = holder;
>                 mGameView = gameView;
>                 mRunning = true;
>         }
>
>         public void setRunning(boolean running) {
>                 mRunning = running;
>         }
>
>         public SurfaceHolder getSurfaceHolder() {
>                 return mHolder;
>         }
>
>         @Override
>         public void run() {
>                 Canvas c;
>                 while(mRunning) {
>                         c = null;
>                         try {
>                                 Thread.sleep(0);
>                                 c = mHolder.lockCanvas();
>                                 synchronized(mHolder) {
>                                         mGameView.updatePhysics();
>                                         mGameView.onDraw(c);
>                                 }
>                         } catch(InterruptedException ex) {
>                         } finally {
>                                 if(c != null) {
>                                         mHolder.unlockCanvasAndPost(c);
>                                 }
>                         }
>                 }
>         }
>
> }
-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Reply via email to