3
\$\begingroup\$

I'm using Surfaceview in my Android app. I have two threads, one for UI and another for rendering and updading the UI.

I am limiting my frames per second to 30 fps and have employed frame-skipping to a max of 3 frames.

I am using delta to update my sprite positions.

All works as it should apart from one thing - the sprites don't move entirely 'smoothly'

They seem to 'jerk' randomly. And I'm just wondering, is this just the way surfaceview is? I'm guessing it's when the device is doing something else? Can I give priority to my app somehow or does anyone have any tips (apart from switching to OpenGL !!) On optimising surfaceView and to get it to run a bit smoother?

Thanks

I've removed everything from my onDraw() and update() methods and just put 1 small sprite in onDraw() and added 1 to the sprites X position in my update() method. The sprite still 'jerks' along, hard to explain, but it moves smoothly for a little while then there's a slight stutter.

Edit: Some code as requested..... (I've given my onDraw, update and run methods here)

@Override
protected void onDraw(Canvas canvas) {
   canvas.drawBitmap(scaledBackground, 0, 0, null);
   canvas.drawBitmap(scaledSmallCloud, cloudX, cloudY, null};
}

public void update() {
   cloudX++;
}

protected class preThread extends Thread {

   private SurfaceHolder mySurfaceHolder;
   private Context myContext;
   // flag to hold game state
   private boolean running;

   public preThread(SurfaceHolder surfaceholder, Context context, Handler handler) {                          // Constructor
      mySurfaceHolder=surfaceholder;
      myContext=context;
      res = myContext.getResources();
   }


   public void setRunning(boolean running) {
      this.running = running;
   }

    @Override
    public void run() {
        Canvas c;
        while (running) {
            c=null;
            timestart = System.currentTimeMillis(); //Get time at start of loop for FPS calc
            try { 
                c = mySurfaceHolder.lockCanvas(null);                                 
                synchronized(mySurfaceHolder) {
                    onDraw(c);       
                    update();
                }
            } finally {
                if (c != null) {
                    mySurfaceHolder.unlockCanvasAndPost(c);                      
                }
            }

            //Frame limiting and skipping calcs
            timeend = System.currentTimeMillis();                               //Get end time of current frame
            frametime = timeend-timestart;                                      //Time frame took to render and update
            sleepfor = (int) ((1000/fps)-frametime);                            // this is the time that the thread will sleep for if <target time 

            if (sleepfor>0){                                                    // If the 'sleepfor' variable is >0 then set the thread to sleep for it's value (expressed in ms)
               try {
                   MainThread.sleep(sleepfor);                                  //send thread to sleep for value of sleepfor (determined above).
               } catch (InterruptedException e) {}                              //in case of exception
            }                                                                   //close if statement
            while (sleepfor<0 && framesskipped<maxframesskipped) {              //if sleepfor is < 0 (ie, frame took longer to render than target time and the maxframesskipped has not reached it's limit)
                update();                                                       //Update animation variables without rendering to the screen while these conditions are met 
                sleepfor+=33.33333333333333;                                    //time to sleep plus the time frame took to render
                framesskipped++;                                                //add one to framesskipped variable so this only skips a certain number of frames
                //Log.v("Timer","frame skipped!");
                //frame=0;
            }
        }
    }

This is basically a really stripped down onDraw() which simply displays a static background and a single moving sprite.

I have removed my delta-sprite movement to rule that out and am simply incrementing the sprite's x position by an explicit value (1).

I've also tried the following:

  • Removing the drawing of the background image

  • Changing 'overridden' onDraw() to doDraw and moving it to the preThread class.

  • Increasing upper frame rate limit to 60fps (as @Lumis suggested in comments)

  • I've removed my frame limiting and skipping so the game just runs flat out at the max frame rate - this gives a slightly different effect, less of a 'quick skip' and more of an intermittent prolonged 'slow down', then 'speed up' of the sprites.

What actually happens - the sprite does move smoothly (even at 30 fps), but every now and then (more often than I would like) - it kind of 'lags' or 'jerks', it's a small effect but is extremely annoying.

Any more suggestions would be very much appreciated - thank you again!

\$\endgroup\$
13
  • \$\begingroup\$ Do you allocate any memory at runtime? \$\endgroup\$ Feb 12, 2013 at 20:31
  • \$\begingroup\$ I create all my objects either during the constructor or during an 'initialise()' method which is called from my Activity class - then the app hits the run() method during which onDraw() and my game logic update method are run. No objects are being created within this loop - is that what you meant @NickHartung? Thanks \$\endgroup\$ Feb 12, 2013 at 21:06
  • \$\begingroup\$ Pretty much. You don't want to allocate any memory at runtime because this can cause the garbage collector to be fired and eat up a lot of CPU time. It doesn't sound like you do though. Otherwise it's difficult to say what's going on here without more information, or actually seeing the issue. I don't think it's a problem with the surfaceView class though. I use it for my game which runs smoothly as 60fps. \$\endgroup\$ Feb 12, 2013 at 21:13
  • \$\begingroup\$ Yeah, I think you're right, it almost seems like the phone is doing other things in the background, don't get me wrong, it isn't that bad and probably wouldn't be noticeable in normal use, it's just annoying as I know it's there. I've just spent the last 2 hours optimising my code, but nothing seems to make any different. \$\endgroup\$ Feb 12, 2013 at 22:07
  • \$\begingroup\$ I'd be curious to see the code for the dripped down version. I doubt surfaceview is to blame. It's possible this is the result of separating the update/draw threads, if either is hanging on a resource from the other. Maybe try a single thread that runs through update then draw and see if it's still happening. Another thing definitely worth trying is run it on another phone or emulator. Background processes might be causing problems . Also possible that you're not using the delta in the manner you think you are. \$\endgroup\$ Feb 12, 2013 at 22:25

2 Answers 2

3
\$\begingroup\$

System.currentTimeMillis() should be changed to System.nanoTime() / 1000000;

currentTimeMillis has an issue in it that sometimes comes back with a weird number and in games causes twitching in animations and graphics

\$\endgroup\$
1
\$\begingroup\$

This solved the problem for me:

Paint bitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);

canvas.drawBitmap(scaledBackground, 0, 0, bitmapPaint);
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Stack Exchange! You might want to consider adding a bit of detail so the user is a little more knowledgeable on what's going on. :) \$\endgroup\$ Feb 13, 2013 at 1:16
  • 1
    \$\begingroup\$ Hey @chris, thanks for the suggestion, unfortunately, it didn't make any difference to my choppy sprites :-( \$\endgroup\$ Feb 13, 2013 at 2:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .