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