views:

383

answers:

2

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;
}
+4  A: 

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
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
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
They aren't in 2 separate threads, same thread different functions. Can you add 2 threads to the same activity?
Cameron
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
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
Thank you so much! It will take me a while to implement this but I think it will be a start!A+ Andy!
Cameron
Wow, way to go Andy, that was smart! I will look at this too!
Julian Assange
A: 

I think it's about Garbage collector

Steven Shih
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