Android 2d canvas game: FPS jitter problem

I based my game on a demonic landing demo, although it was highly modified, and I can get about 40-50 frames per second, but the problem is that it fluctuates between 40-50fps so much that it causes motion graphics to shake! It’s very annoying and makes my game really bad, when in fact it works with a good frame rate.

I tried to set the priority of the stream higher, but it only worsened it ... now it will fluctuate between 40-60fps ...

I thought to limit the FPS to about 30 so that it was constant. Is this a good idea and does anyone else have experience or another solution?

Thanks!

This is my loop cycle

@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
source share
5 answers

Do not base the refresh rate of game logic (moving an object, etc.) on the frame rate. In other words, enter the drawing and logical update code into two separate components / threads. Thus, your game logic is completely independent of your frame rate.

A logical update should be based on how much time has passed since the last update (call delta on it). Therefore, if you have an object moving at a speed of 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 is lagging behind, it will not affect the speed of your object, since the delta will simply be larger, forcing the object to move further to compensate (in some cases there are complications, but that’s the point).

And this is one way to calculate the delta in your logic update object (performed in a certain sequence of threads):

 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 this helps! Ask if you have any other questions; I myself made Android games. :)


Code example:

Copy these two snippets (1 activity and 1 view) and run the code. The result should be a white dot smoothly falling on your screen, regardless of your FPS. The code looks rather complicated and long, but actually it is quite simple; comments should explain everything.

This class of activity is not very important. You can ignore most of the code.

 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 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 interesting stuff!

 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 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 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 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 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 been since last update update(delta); // Call update with our delta time1 = time2; // Update our time variables. } } }.start(); // Start THREAD_B } // Method that called by the activity public void setActive(boolean active) { this.active = active; } } 
+21
source

I think that maybe something is wrong with some of the code above, but rather inefficiency. I am talking about this code ...

  // The important part! // This starts another thread (let 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 been since last update update(delta); // Call update with our delta time1 = time2; // Update our time variables. } } }.start(); // Start THREAD_B } 

In particular, I am thinking of the following lines ...

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

It seems to me that just turning off the thread without doing anything is a waste of valuable processing time, when in fact what you want to do is perform the updates, and then if the updates take less time than 25 milliseconds, then the sleep thread for the difference is what was used during the update, and 25 milliseconds (or regardless of the frame rate you choose). Thus, the update will occur during the rendering of the current frame and will be completed, therefore, the update of the next frame will use the updated values.

The only problem I can think of is that some kind of synchronization is required so that the current frame rendering does not use partially updated values. Perhaps update a new instance of your value set, and then create a new instance of the current instance just before rendering.

I think that I remember reading something in a graphic book that you need to perform as many updates as you can, staying within the desired frame rate, and then only perform screen updates.

This, of course, will require one thread to manage updates - if you use SurfaceView, the render is controlled by this thread when you block the canvas (theoretically, in my opinion, one way or another).

So, in code, it will 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 those who use this will surely help us all learn something ...

rpbarbati

+3
source

I think this is a garbage collector

0
source

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

0
source

I have a similar problem, jittering makes large movements of objects jagged. Although the β€œspeed” is the same, different step lengths make the movements look nervous. Broody. You say that SurfaceView is a beter, however it is not so after Android 3.0, since View HW is accelerated, but the canvas returned by .lockCanvas is not. Stephen - Yes, it probably causes problems, but it's easy to spot. / Jacob

0
source

Source: https://habr.com/ru/post/1316583/


All Articles