4

I based my game off of the lunar lander demo, although heavily modified, and I can get around 40-50fps but the problem is it fluctuates between 40-50fps so much that it causes the moving graphics to jitter! Its very annoying and makes my game look really shitty when in fact its running at a good frame rate.

I tried setting the thread priority higher but that just made it worse... now it will fluctuate between 40-60fps...

I was thinking of limiting the FPS to about 30 so that it will be constant. Is this a good idea and does anyone else have experience or a different solution?

Thanks!

This is my run loop

@Override
    public void run() {
        while (mRun) {
            Canvas c = null;
            try {
                c = mSurfaceHolder.lockCanvas(null);
                synchronized (mSurfaceHolder) {
                    if(mMode == STATE_RUNNING){

                        updatePhysics();
                    }
                    doDraw(c);
                }
            } finally {
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
        }

private void updatePhysics() {

        now = android.os.SystemClock.uptimeMillis();

        elapsed = (now - mLastTime) / 1000.0;

        posistionY += elapsed * speed;
        mLastTime = now;
}
Cameron
  • 1,612
  • 2
  • 16
  • 24

5 Answers5

22

Don't base your game's logic (object movement, etc.) updating rate on the framerate. In other words, put your drawing and logic updating code in two separate components/threads. This way your game logic is completely independent from your framerate.

Logic updating should be based on how much time has passed since the last update (let's call it delta). Therefore, if you have an object moving at 1px/millisecond, then during each update your object should do something like this:

public void update(int delta) {
    this.x += this.speed * delta;
}

So now even if your FPS lags, it won't affect your object's movement speed, since the delta will just be larger, making the object move farther to compensate (there are complications in some cases, but that's the gist of it).

And this is one way of calculating delta within your logic updating object (running in some thread loop):

private long lastUpdateTime;
private long currentTime;

public void update() {
    currentTime = System.currentTimeMillis();
    int delta = (int) (currentTime - lastUpdateTime);
    lastUpdateTime = currentTime;
    myGameObject.update(delta); // This would call something like the update method above.
}

Hope that helps! Please ask if you have any other questions; I've been making Android games myself. :)


Sample code:

Copy these two snippets (1 activity and 1 view) and run the code. The result should be a white dot smoothly falling down your screen, no matter what your FPS is. The code looks kinda complicated and long, but it's actually quite simple; the comments should explain everything.

This activity class isn't too important. You can ignore most of the code in it.

public class TestActivity extends Activity {

    private TestView view;

    public void onCreate(Bundle savedInstanceState) {
        // These lines just add the view we're using.
        super.onCreate(savedInstanceState);
        setContentView(R.layout.randomimage);
        RelativeLayout rl = (RelativeLayout) findViewById(R.id.relative_layout);
        view = new TestView(this);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                10000, 10000);
        rl.addView(view, params);

        // This starts our view's logic thread
        view.startMyLogicThread();
    }

    public void onPause() {
        super.onPause();
        // When our activity pauses, we want our view to stop updating its logic.
        // This prevents your application from running in the background, which eats up the battery.
        view.setActive(false);
    }
}

This class is where the exciting stuff is!

public class TestView extends View {

    // Of course, this stuff should be in its own object, but just for this example..
    private float position; // Where our dot is
    private float velocity; // How fast the dot's moving

    private Paint p; // Used during onDraw()
    private boolean active; // If our logic is still active

    public TestView(Context context) {
        super(context);
        // Set some initial arbitrary values
        position = 10f;
        velocity = .05f;
        p = new Paint();
        p.setColor(Color.WHITE);
        active = true;
    }

    // We draw everything here. This is by default in its own thread (the UI thread).
    // Let's just call this thread THREAD_A.
    public void onDraw(Canvas c) {
        c.drawCircle(150, position, 1, p);
    }

    // This just updates our position based on a delta that's given.
    public void update(int delta) {
        position += delta * velocity;
        postInvalidate(); // Tells our view to redraw itself, since our position changed.
    }

    // The important part!
    // This starts another thread (let's call this THREAD_B). THREAD_B will run completely
    // independent from THREAD_A (above); therefore, FPS changes will not affect how
    // our velocity increases our position.
    public void startMyLogicThread() {
        new Thread() {
            public void run() {
                // Store the current time values.
                long time1 = System.currentTimeMillis();
                long time2;

                // Once active is false, this loop (and thread) terminates.
                while (active) {
                    try {
                        // This is your target delta. 25ms = 40fps
                        Thread.sleep(25);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }

                    time2 = System.currentTimeMillis(); // Get current time
                    int delta = (int) (time2 - time1); // Calculate how long it's been since last update
                    update(delta); // Call update with our delta
                    time1 = time2; // Update our time variables.
                }
            }
        }.start(); // Start THREAD_B
    }

    // Method that's called by the activity
    public void setActive(boolean active) {
        this.active = active;
    }
}
Andy Zhang
  • 8,285
  • 1
  • 37
  • 24
  • Hi Andy, I updated my question to include the game loop. Is this the way you were talking about implementing it? I have two separate functions, one for the physics, and one for drawing. Inside the physics I have the elapsed time or "delta" as how you described it in your answer. For some reason I still get jittering of the graphics. What I think is happening is the falling objects are not falling at a constant rate because delta keeps fluctuating. – Cameron Jul 22 '10 at 21:41
  • Are you sure the physics and drawing functions are in two separate threads? Having two separate threads will make them run time-independent of each other. As for the jittering, are you sure you're calculating the delta correctly (the time between now and the last update)? Also make sure all the physics in your game is dependent on that delta, instead of how many times update has been called. If you don't mind, please post some of the code in question so that I can offer more detailed advice. I'll try to find some of my code to post. Thanks! – Andy Zhang Jul 22 '10 at 22:02
  • They aren't in 2 separate threads, same thread different functions. Can you add 2 threads to the same activity? – Cameron Jul 22 '10 at 22:07
  • Oh, that's the problem then. They need to run in 2 separate threads. Here, I'll post some code I just wrote above and explain it in more detail there (in a couple of minutes). – Andy Zhang Jul 22 '10 at 22:17
  • I just took a look at your code. I noticed your call sequence was "update, draw, repeat." Thus, your call to update will of course be delayed if drawing takes longer than usual. This is where having them in 2 separate threads comes in handy. The drawing can take however long it wants, but the updating will still update at the same rate, resulting in smooth logic. If it still lags, the only remaining reason would be if the phone was literally incapable of processing your logic/drawing that fast. If so, props to your high-end graphics-intensive game. ^^ – Andy Zhang Jul 22 '10 at 22:43
  • Thank you so much! It will take me a while to implement this but I think it will be a start! A+ Andy! – Cameron Jul 22 '10 at 22:51
  • Wow, way to go Andy, that was smart! I will look at this too! – Curtain Aug 24 '10 at 19:44
  • Good answer. Things have progressed a bit since 2010; now you can use Choreographer to improve smoothness further. See https://source.android.com/devices/graphics/architecture.html#loops – fadden Nov 24 '14 at 18:54
3

I am thinking there might be, not really something wrong with some of the above code, but rather an inefficiency. I am talking about this code...

   // The important part!
// This starts another thread (let's call this THREAD_B). THREAD_B will run completely
// independent from THREAD_A (above); therefore, FPS changes will not affect how
// our velocity increases our position.
public void startMyLogicThread() {
    new Thread() {
        public void run() {
            // Store the current time values.
            long time1 = System.currentTimeMillis();
            long time2;

            // Once active is false, this loop (and thread) terminates.
            while (active) {
                try {
                    // This is your target delta. 25ms = 40fps
                    Thread.sleep(25);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }

                time2 = System.currentTimeMillis(); // Get current time
                int delta = (int) (time2 - time1); // Calculate how long it's been since last update
                update(delta); // Call update with our delta
                time1 = time2; // Update our time variables.
            }
        }
    }.start(); // Start THREAD_B
}

Specifically, I am thinking about the following lines...

// This is your target delta. 25ms = 40fps
Thread.sleep(25);

It seems to me that just having the thread hang out doing nothing is a waste of valuable processing time, when in fact what you want to be doing is performing the updates, then, if the updates have taken less time than the 25 millis, then sleep the thread for the difference of what was used during the update and 25 millis (or whatever your chosen frame rate is). In this way the update will happen while the current frame is being rendered, and will be completed so the next frame update uses the updated values.

The only problem I can think of here is that some kind of syncronization will need to occur so that the current frame render does not use partially updated values. Perhaps update into a new instance of your set of values, and then make the new instance the current instance just before rendering.

I think I remember reading something in a graphics book about the goal being to perform as many updates as you can while staying within your desired frame rate, then, and only them, perform a screen update.

This of course will require one thread to drive the updates - if you use a SurfaceView, the render is controlled by this thread when you lock the canvas (in theory, according to my understanding anyway).

So, in code, it would be more like...

// Calculate next render time
nextRender = System.currentTimeInMillis() + 25;

while (System.currentTimeInMillis() < nextRender)
{
    // All objects must be updated here
    update();

    // I could see maintaining a pointer to the next object to be updated,
    // such that you update as many objects as you can before the next render, and 
    // then continue the update from where you left off in the next render...
}

// Perform a render (if using a surface view)
c = lockCanvas() blah, blah...
// Paint and unlock

// If using a standard view
postInvalidate();

Good luck and any feedback from anyone using this would surely help us all learn something...

rpbarbati

0

I have a similar issue, the jitter makes large object moves look uneven. Even though the "speed" is the same, different lengths of steps make the movements look jumpy. Broody - You say a SurfaceView is beter, however, this is not true after Android 3.0 as the View is HW accelerated but the canvas returned by .lockCanvas is not. Steven - Yes, this is likely causing poroblems, but is easy to detect. /Jacob

Jacob L
  • 71
  • 5
0

I think it's about Garbage collector

Steven Shih
  • 645
  • 1
  • 10
  • 22
  • Yes that usually is the problem, but I had taken steps to ensure nothing was allocated or destroyed during runtime. Anyways I fixed it, it was something to do with my timing, I was using the wrong system time in the wrong places which caused inconsistent timing. Also there was a unit conversion problem, turning the double -> int, I was doing it too early and making rounding errors, so I waited until right before using it in onCanvas.draw to convert. – Cameron Oct 21 '10 at 07:20
0

I would use SurfaceView instead of View if your game is action heavy. If you don't need to update the GUI rapidly then View is fine but for 2D games it's always better to use SurfaceView.

broody
  • 697
  • 1
  • 6
  • 17