Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (769)
Games in Android Showcase (230)
games submitted by our members
Games in WIP (855)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Loop and FPS (one more newbie problem...)  (Read 1794 times)
0 Members and 1 Guest are viewing this topic.
Offline AndroidAddict

Junior Newbie





« Posted 2014-11-03 19:11:43 »

Hi all

I've a problem with my game loop, sometimes the render is jerking, the drawing is not perfect

- In my game, the background is scrolling to the left indefinitely, the drawing should be perfect, but the render is not constant (regular)
- The garbage collector is never in action
- The background is composed by sprite (bmp) but even with only one bmp it is still jerking
- I've tried the loop on http://www.java-gaming.org/topics/game-loops/24220/view.html and this loop is also jerking (less than mine...)

I'm quasi sure that the loop I'm using is not perfect in term of timing (sleeping thread)

can you help me please?




here is the code I use:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
public class GameLoopThread extends Thread
{
   private final static int    MAX_FPS = 60;               // desired fps
   private final static int   MAX_FRAME_SKIPS = 5;         // maximum number of frames to be skipped
   private final static int   FRAME_PERIOD = 1000 / MAX_FPS;   // the frame period
   private static boolean running;                           // flag to hold game state

   private SurfaceHolder surfaceHolder;   // Surface holder that can access the physical surface
   private GameView gameview;      // The actual view that handles inputs and draws to the surface

   public GameLoopThread(SurfaceHolder surfaceHolder, GameView gameview)
   {
      super();
      this.surfaceHolder = surfaceHolder;
      this.gameview = gameview;
   }
   
   public static void setRunning(boolean runningstate)
   {
      running = runningstate;
   }

   @Override
   public void run()
   {
      Canvas canvas;

      long beginTime;      // the time when the cycle begun
      long timeDiff;      // the time it took for the cycle to execute
      int sleepTime;      // ms to sleep (<0 if we're behind)
      int framesSkipped;   // number of frames being skipped

      sleepTime = 0;
     
     
      while (running)
      {
         canvas = null;
         // try locking the canvas for exclusive pixel editing in the surface
         try
         {
            canvas = surfaceHolder.lockCanvas();
            synchronized (surfaceHolder)
            {
               beginTime = System.currentTimeMillis();
               framesSkipped = 0;   // resetting the frames skipped

               
               
               
               gameview.update();      // update game state
               gameview.render(canvas);   // render state to the screen draws the canvas on the panel

               
               
               
               timeDiff  = System.currentTimeMillis() - beginTime;      // calculate how long did the cycle take
               sleepTime = (int)(FRAME_PERIOD - timeDiff);      // calculate sleep time

               if (sleepTime > 0)
               {
                  // if sleepTime > 0 we're OK
                  try
                  {
                     Thread.sleep(sleepTime);   // send the thread to sleep for a short period very useful for battery saving
                  }
                  catch (InterruptedException e) {}
               }


               while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS)
               {
                  // we need to catch up
                  gameview.update(); // update without rendering
                  sleepTime += FRAME_PERIOD;   // add frame period to check if in next frame
                  framesSkipped++;
               }
            }
         }
         finally
         {
            // in case of an exception the surface is not left in an inconsistent state
            if (canvas != null)
            {
               surfaceHolder.unlockCanvasAndPost(canvas);
            }
         }   // end finally
      }

   }

}


thanks for having reading this

Bests regards
Offline jmguillemette
« Reply #1 - Posted 2014-11-03 20:02:50 »

why are you doing this?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
 timeDiff  = System.currentTimeMillis() - beginTime;      // calculate how long did the cycle take
               sleepTime = (int)(FRAME_PERIOD - timeDiff);      // calculate sleep time

               if (sleepTime > 0)
               {
                  // if sleepTime > 0 we're OK
                  try
                  {
                     Thread.sleep(sleepTime);   // send the thread to sleep for a short period very useful for battery saving
                  }
                  catch (InterruptedException e) {}
               }


You should not make your thread sleep.. but rather adjust how much you move your models based on relative time.

This may be the cause of your jerkiness.

-=Like a post.. give the author a medal!=-
Offline AndroidAddict

Junior Newbie





« Reply #2 - Posted 2014-11-03 20:05:30 »

why are you doing this?

because sometimes sleepTimes is < 0

You should not make your thread sleep.. but rather adjust how much you move your models based on relative time.

And how to do this?

any example please?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline jmguillemette
« Reply #3 - Posted 2014-11-03 20:09:07 »

say you want to move forward at a rate of 10 feet per second.

but only 0.0001 of a second has gone by since you last drew the screen.

thus deltaTime * rateOfMovement * transformation = deltaPosition.
currentPostion + deltaPosition = newPosition;

if deltaTime is 0.. your newPosition and currentPosition will be equal.

Follow this approach instead of waiting for a fixed amount of time to have past.
Youll end up with a smooth animation this way.

j.

-=Like a post.. give the author a medal!=-
Offline AndroidAddict

Junior Newbie





« Reply #4 - Posted 2014-11-05 10:34:20 »

thanks for the reply, but still jerking, is my code so wrong?

tested on a galaxy note 2, the garbage collector seems not be the problem (cause no log about it)

Is there some specifics implementations for ANDROID to have a smooth render?

Best regards
---------------

The Game Loop:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;

// The Main thread which contains the game loop. The thread must have access to the surface view and holder to trigger events every game tick.
public class GameLoopThread extends Thread
{
   private static final String TAG = GameLoopThread.class.getSimpleName();

   private SurfaceHolder surfaceHolder;   // Surface holder that can access the physical surface
   private GameView      gameview;       // The actual view that handles inputs and draws to the surface
   private Canvas        canvas;
   
   final   int            TARGET_FPS = 40;
   final   long           OPTIMAL_TIME = 1000000000 / TARGET_FPS;  
   private static boolean running;                            // flag to hold game state

   int     lastFpsTime;
   int     fps;


   public GameLoopThread(SurfaceHolder surfaceHolder, GameView gameview)
   {
      super();
      this.surfaceHolder = surfaceHolder;
      this.gameview = gameview;
   }

   public static void setRunning(boolean runningstate)
   {
      running = runningstate;
   }

   @Override
   public void run()
   {
      Log.d(TAG, "Starting game loop");

      long lastLoopTime = System.nanoTime();

      while (running)      // keep looping round til the game ends
      {
         long now = System.nanoTime();
         canvas   = null;

         try         // try locking the canvas for exclusive pixel editing in the surface
         {
            canvas = surfaceHolder.lockCanvas();
            synchronized (surfaceHolder)
            {
               // work out how long its been since the last update, this will be used to calculate how far the entities should move this loop
               long updateLength = now - lastLoopTime;
               lastLoopTime      = now;
               double delta      = updateLength / ((double)OPTIMAL_TIME);

               // update the frame counter
               lastFpsTime += updateLength;
               fps++;

               // update our FPS counter if a second has passed since we last recorded
               if (lastFpsTime >= 1000000000)
               {
                  System.out.println("(FPS: "+fps+")");
                  lastFpsTime = 0;
                  fps         = 0;
               }

               gameview.update(delta);      // update game state
               gameview.render(canvas);   // render state to the screen draws the canvas on the panel
               
               int sleeptime = (int)((lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000);  // remember this is in ms, whereas our lastLoopTime etc. vars are in ns.

               if(sleeptime>0)
               {
                  try
                  {
                     Thread.sleep( sleeptime );
                  }
                  catch (InterruptedException e) {}
               }
               else
               {
                  // WHAT TO DO HERE??? nothing ???
                  //Log.d("sleeptime" , String.valueOf(sleeptime));
               }
            }
         }
         finally
         {
            if (canvas != null)            // in case of an exception the surface is not left in an inconsistent state
            {
               surfaceHolder.unlockCanvasAndPost(canvas);
            }
         }
      }
   }

}


The Sprite class

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.Log;

public class SingleBlock {

   private Context context;

   // sprites

   public ArrayList<Bitmap> bmp_sprite = new ArrayList<Bitmap>();

   // data

   public static double x = 0;           // x position of the sprite
   public static double y = 50;          // y position of the sprite
   public        int    speed = 10;      // speed of the sprite
   public        int    direction = -1;  // -1 means moving to the left, and 1 means movingto the right

   //________________________
   // constructeur           |
   //________________________|

   public  SingleBlock(Context context)
   {
      this.context            = context;
      LoadSprite();
   }

   public void update(double delta)
   {
      // movethe sprite

      if(direction == -1) { x-= speed*delta; }
      if(direction == 1)  {x+= speed*delta;  }

      //change direction

      if(x<=0)    { direction = 1; }
      if(x>=1000) { direction = -1; }
   }

   //________________________
   // Draw                   |
   //________________________|

   public void Draw(Canvas canvas)
   {
      canvas.drawBitmap(bmp_sprite.get(1), (int)x , (int)y , null);
   }

   //________________________
   // LoadSprite             |
   //________________________|
   public void LoadSprite()
   {
      for(int i = 0;i<= 2; i++)
      {
         AssetManager assetManager = context.getAssets();
         InputStream inputStream   = null;
         try
         {
            inputStream = assetManager.open(ScreenConf.asset_sub_directory + "/" + "block" + String.valueOf(i) + ".png");
         }
         catch (IOException e) { e.printStackTrace(); }

         Bitmap bmp_temp = BitmapFactory.decodeStream(inputStream);

         bmp_sprite.add(bmp_temp);
      }
   }
}


And all other class:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

// This is the main surface that handles the ontouch events and draws the image to the screen.

public class GameView extends SurfaceView implements SurfaceHolder.Callback {

   private static final String TAG = GameView.class.getSimpleName();

   private        GameLoopThread gameLoopThread;
   public static  Context        context;
   private static GameView       gameview;
   private static SingleBlock   singleblock;

   public GameView(Context context)
   {
      super(context);

      this.context = context;
      gameview     = this;

      getHolder().addCallback(this);                           // adding the callback (this) to the surface holder to intercept events
      gameLoopThread = new GameLoopThread(getHolder(), this);  // create the game loop thread
      setFocusable(true);                                      // make the GamePanel focusable so it can handle events
   }

   @Override
   public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
   {
   }

   @Override
   public void surfaceCreated(SurfaceHolder holder)
   {
      // at this point the surface is created and we can safely start the game loop

      new ScreenConf(gameview);

      StartNewGame();

      GameLoopThread.setRunning(true);
      gameLoopThread.start();
   }

   @Override
   public void surfaceDestroyed(SurfaceHolder holder)
   {
      Log.d(TAG, "Surface is being destroyed");
      // tell the thread to shut down and wait for it to finish  this is a clean shutdown
      boolean retry = true;
      while (retry)
      {
         try
         {
            gameLoopThread.join();
            retry = false;
         }
         catch (InterruptedException e)
         {
            // try again shutting down the thread
         }
      }
      Log.d(TAG, "Thread was shut down cleanly");
   }

   public static void StartNewGame()
   {
      singleblock = new SingleBlock(context);
   }

   public void render(Canvas canvas)
   {
      if (canvas != null)
      {
         canvas.drawColor(Color.BLACK);
         singleblock.Draw(canvas);
      }
   }

   // This is the game update method. It iterates through all the objects and calls their update method if they have one or calls specific engine's update method./
   public void update(double delta)
   {
      singleblock.update(delta);
   }

}



1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity
{
   /** Called when the activity is first created. */

   private static final String TAG = MainActivity.class.getSimpleName();


   //##################################
   //  onCreate                       #
   //##################################
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);

      requestWindowFeature(Window.FEATURE_NO_TITLE);                                                   // requesting to turn the title OFF
      getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);   // making it full screen

      setContentView(new GameView(this));        // set our GameView as the View

      Log.d(TAG, "View added");
   }

   //##################################
   //  onResume                       #
   //##################################
   @Override
   public void onResume()
   {
      Log.d(TAG, "onResume...");
      super.onResume();  // Always call the superclass method first
   }
   //##################################
   //  onDestroy                      #
   //##################################
   @Override
   protected void onDestroy()
   {
      Log.d(TAG, "Destroying...");
      super.onDestroy();
   }

   //##################################
   //  onStop                         #
   //##################################
   @Override
   protected void onStop()
   {
      Log.d(TAG, "Stopping...");
      super.onStop();
   }
}

Pages: [1]
  ignore  |  Print  
 
 

 
EgonOlsen (1642 views)
2018-06-10 19:43:48

EgonOlsen (1703 views)
2018-06-10 19:43:44

EgonOlsen (1155 views)
2018-06-10 19:43:20

DesertCoockie (1583 views)
2018-05-13 18:23:11

nelsongames (1183 views)
2018-04-24 18:15:36

nelsongames (1706 views)
2018-04-24 18:14:32

ivj94 (2514 views)
2018-03-24 14:47:39

ivj94 (1731 views)
2018-03-24 14:46:31

ivj94 (2812 views)
2018-03-24 14:43:53

Solater (952 views)
2018-03-17 05:04:08
Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-08-22 08:04:08

Deployment and Packaging
by gouessej
2018-08-22 08:03:45

Deployment and Packaging
by philfrei
2018-08-20 02:33:38

Deployment and Packaging
by philfrei
2018-08-20 02:29:55

Deployment and Packaging
by philfrei
2018-08-19 23:56:20

Deployment and Packaging
by philfrei
2018-08-19 23:54:46
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!