Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (784)
Games in Android Showcase (234)
games submitted by our members
Games in WIP (858)
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  
  Sudden random jitter when moving an image in game loop  (Read 2589 times)
0 Members and 1 Guest are viewing this topic.
Offline blue03corner

Senior Newbie


Exp: 2 years



« Posted 2019-03-02 06:36:29 »

I'm getting a random jitter when moving an image in a loop. I'm I the only one experiencing this?

I tried various attempts to fix this problem like changing my game loop to tick based instead of time based loop, Changing JFrame to AWT Frame, putting the game loop in EDT, use JPanel instead of canvas, using two threads to separate update() from draw(), use other jvm like openj9. All these attempts didn't solve the issue.

After all of those attempts, I found a solution by using the full screen exclusive mode. But, my game needs to be switched in windowed mode.

I found a solution for windowed mode by manually activating the openGL pipeline.
1  
System.setProperty("sun.java2d.opengl", "True");



Seems like everything is fine, when I remove the game loop and just put the update() and draw() method in an infinite loop, the image moves smoothly and the fps stays at 60 fps without a game loop. How's that happened? When I don't activate openGL or don't use full screen exclusive mode, the fps is so fast. Same goes if I activate the d3d pipeline. Does this happen to all machines or it depends on the machine? If this happens in all machine then this is a great solution to my problem, but I think it varies. I hope somebody can answer this question because I couldn't find any article regarding to this. I tried reading some article on oracle and still couldn't find the answer.

https://docs.oracle.com/javase/8/docs/technotes/guides/2d/flags.html

https://docs.oracle.com/javase/8/docs/technotes/guides/2d/new_features.html

https://www.oracle.com/technetwork/java/perf-graphics-135933.html


When I put back the game loop like this:

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  
while(true)
        {
               
              currentTime = System.nanoTime();
           totalTime += currentTime - initialTime;
              t2 += currentTime - initialTime;
              initialTime = currentTime;
             
              if( totalTime > NANOS_PER_SECOND ) {
                totalTime -= NANOS_PER_SECOND;
                st.fps = frames;
                frames = 0;
              }
               
           
              if(t2 > FRAMES_PER_NANO)
              {
                  st.update_Game();
        st.drawGraphics();
                  frames += 1;
                  t2 -= FRAMES_PER_NANO;
              }

           //st.update_Game();
           //st.drawGraphics();
               
              Thread.yield();
        }


I put the Update_game() and drawGraphics() in if statement, and the image starts to randomly jitter again even I manually activate the openGL pipeline.

When I do my game loop like this with openGL pipeline activated

1  
2  
3  
4  
5  
while(true)
        {
       st.update_Game();
       st.drawGraphics();
        }

The image moves smoothly.

This kind of code arrangement in game loop also has no jitter as long as the openGL pipeline is activated.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
while(true)
        {
               
              currentTime = System.nanoTime();
           totalTime += currentTime - initialTime;
              t2 += currentTime - initialTime;
              initialTime = currentTime;
             
              if( totalTime > NANOS_PER_SECOND ) {
                totalTime -= NANOS_PER_SECOND;
                st.fps = frames;
                frames = 0;
              }

           st.update_Game();
           st.drawGraphics();
                          frames += 1;

                         Thread.yield();
               
        }


As you can see I didn't put the update_Game(), drawGraphics() and frames += 1; in an if statement this time. In my machine this loop frame count is 60 as long as the openGL pipeline is activated or full screen exclusive mode is activated. Otherwise, It will go up to a thousand frame counts.

Is there something wrong with my loop? Or the main loop is being block by other third party thread like gc or EDT? Or it's just java is so bad doing active rendering when not accelerated?

Here's my test code in case you want to try if random jitter happens in your machine.

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  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsDevice;
import java.awt.GraphicsConfiguration;
import java.awt.Color;
import java.awt.Toolkit;

import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.awt.image.BufferStrategy;

import java.awt.geom.AffineTransform;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import javax.imageio.ImageIO;

import java.io.File;

import java.util.Random;

public class ScreenTest extends JFrame
{
   private static ScreenTest st;
   
   private Graphics2D g2dbf;
   //
   private Graphics g;
   
   private VolatileImage bgImage;
   
   private Canvas frameCanvas;
   //JFrame size
   private int frameWidth,frameHeight;
   
   //Identity transform
   private AffineTransform identityTrans;
   //Object transform
   private AffineTransform objTrans;
   //
   private BufferStrategy bufferS;
   
   //platform's graphics attributes...
    GraphicsEnvironment ge;
    GraphicsDevice gd;
    GraphicsConfiguration gc;
   
   public double posX, posY;
   private double velX, velY;
   public double recentPosY;
   
   int fps;
   private BufferedImage myImg;
   
   Random rand = new Random();
   
   ScreenTest()
   {
      //JFrame properties
      setTitle("Screen Test");
      setIgnoreRepaint(true);
      setResizable(false);
      setUndecorated(true);
     
      // Get platform's graphics attributes...
      ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      gd = ge.getDefaultScreenDevice();
      gc = gd.getDefaultConfiguration();
     
      //Set transform to identity transform
      identityTrans = new AffineTransform();
      //object transform
      objTrans = new AffineTransform();
       
      //Exclusive Full Screen Mode
     
      /*
      gd.setFullScreenWindow( this );
        if( gd.isDisplayChangeSupported() ) {
      DisplayMode dm = gd.getDisplayMode();
      frameWidth = dm.getWidth();
      frameHeight = dm.getHeight();
     
        gd.setDisplayMode( new DisplayMode( frameWidth, frameHeight, dm.getBitDepth(), dm.getRefreshRate() ));
        }
      */

     
     
      //Simulated full screen mode
      //Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        //frameWidth = (int)screenSize.getWidth();
        //frameHeight = (int)screenSize.getHeight();
     
      //Windowed Mode
      frameWidth = 640;
        frameHeight = 480;
     
      try { myImg = ImageIO.read(new File("img.png")); }
      catch(IOException e){ System.err.println(e); }
     
      posX = frameWidth/2;
      posY = frameHeight/2;
     
      velX = 0;
      velY = -6;
      recentPosY = posY;
     
     
      setSize(frameWidth,frameHeight);
      setVisible(true);
     
     
      createBufferStrategy( 2 );
        bufferS = getBufferStrategy();
     
     
     
      //back buffer image
      bgImage = gc.createCompatibleVolatileImage(frameWidth,frameHeight);
     
   }
   
   void update_Game()
   {
      updateImg();
   }
   
   void drawGraphics()
   {
      try
      {
         String str1 = String.format("Velocity X: %.2f",velX);
            String str2 = String.format("Velocity Y: %.2f",velY);
         
         g2dbf = bgImage.createGraphics();
            g2dbf.setTransform(identityTrans);
            //set background
            g2dbf.setColor(Color.BLACK);
            g2dbf.fillRect(0,0,frameWidth,frameHeight);
         
          drawImg();
             
           
          //print some status information
            g2dbf.setColor(Color.WHITE);
            g2dbf.drawString("Image: " + Math.round(posX) + "," +
                Math.round(posY) , 5, 40);
           g2dbf.drawString(str1, 5, 60);
            g2dbf.drawString(str2, 5, 80);
         
         g2dbf.drawString( String.format( "FPS: %s", fps ), 5, 140 );
         
         g = bufferS.getDrawGraphics();
          g.drawImage(bgImage,0,0,null);
         
          if( !bufferS.contentsLost() ) bufferS.show();
         Toolkit.getDefaultToolkit().sync();
      }
      finally
      {
         if(g != null) g.dispose();
         if(g2dbf != null) g2dbf.dispose();
      }
   }

   synchronized void drawImg()
   {
      g2dbf.setTransform(identityTrans);
      objTrans.setToIdentity();
      objTrans.translate(posX, posY);
      g2dbf.drawImage(myImg,objTrans,null);
   }
   
   synchronized void updateImg()
   {
     
      posX += velX;
      posY += velY;
     
        if (posX < -70) posX = frameWidth + 10;
        else if (posX > frameWidth + 10) posX = -10;
     
        if (posY < -10) posY = frameHeight + 10;
        else if (posY > frameHeight + 10) posY = -10;
     
   }
   
   public static void main(String[]args) throws InterruptedException,InvocationTargetException
   {
      System.setProperty("sun.java2d.opengl", "True");
     
      //System.setProperty("sun.java2d.d3d", "True");
     
      SwingUtilities.invokeAndWait(new Runnable(){
         @Override
         public void run()
         {
             st = new ScreenTest();
         }
      });
         
         
         int frames = 0;
         long totalTime = 0,t2 = 0;
         long initialTime = System.nanoTime();
         long currentTime = 0;
         final short FRAMES_PER_SECOND = 60;
         final int NANOS_PER_SECOND = 1_000_000_000;
         final float FRAMES_PER_NANO = NANOS_PER_SECOND / FRAMES_PER_SECOND;
       int count = 0;
         
        while(true)
        {
               
              currentTime = System.nanoTime();
           totalTime += currentTime - initialTime;
              t2 += currentTime - initialTime;
              initialTime = currentTime;
             
              if( totalTime > NANOS_PER_SECOND ) {
                totalTime -= NANOS_PER_SECOND;
                st.fps = frames;
                frames = 0;
              }
               
           
              if(t2 > FRAMES_PER_NANO)
              {
                  st.update_Game();
              st.drawGraphics();
                  frames += 1;
                  t2 -= FRAMES_PER_NANO;
              }

           //st.update_Game();
           //st.drawGraphics();
               
              Thread.yield();
        }
         
         
   }
}
Offline hadezbladez

Senior Devvie


Medals: 5
Projects: 1
Exp: 2 years


Insert Personal Text


« Reply #1 - Posted 2019-03-02 07:37:23 »

I think i had an issue that might be similar to you. i still hadnt got to work on it because i had other project

when two object(you can use as an example rectangle) are going same speed from top to bottom. Some of object will step back at a random time creating jitter
this is the representation of it

do u see those two boxes that are nearly same y-position? those boxes originally were in same y-position but some lags that i dont know causing it

if you still dont catch it i have the raw project of it
you can check it here >> https://github.com/hadezbladez/DodgeThis_project

was your issues same as mine?
Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #2 - Posted 2019-03-02 08:42:42 »

I'll try it later. I edited and added more detail in my question.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #3 - Posted 2019-03-02 20:54:32 »

Update: I just found out that the v-sync settings in my computer causes my game fps to cap at 60 fps because v-sync allow the gamer to synchronize the frame rate of the game with the monitor refresh rate for better stability. My monitor refresh rate is 60hz that's why my game fps is capped at 60 fps. When I turned off v-sync and run my game in an infinite loop without capping the frame rate, my frame count went high even the openGL pipeline was activated.


Still, the random jitter of a moving image is still not solved. I think it's probably because of the loop. I'll try to tweak my game loop more.
Offline hadezbladez

Senior Devvie


Medals: 5
Projects: 1
Exp: 2 years


Insert Personal Text


« Reply #4 - Posted 2019-03-03 01:52:24 »

so the vsync is not much the problem, eh?  Huh  Huh  Huh

i think i had talked about this in this thread
http://www.java-gaming.org/topics/the-game-loop/38861/msg/370944/view/topicseen.html#msg370944  Pointing

i still havent tried it out
Offline philfrei
« Reply #5 - Posted 2019-03-03 02:30:09 »

Lazy response on my part, as I am not taking the time to look at the code right now.

Some basic questions:

Do you employ blocking or synchronization? (Wondering if occasional deadlocks are hidden in your code somewhere.)

Do you always use loose-coupling when passing data between threads?

music and music apps: http://adonax.com
Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #6 - Posted 2019-03-03 04:10:50 »

so the vsync is not much the problem, eh?  Huh  Huh  Huh

i think i had talked about this in this thread
http://www.java-gaming.org/topics/the-game-loop/38861/msg/370944/view/topicseen.html#msg370944  Pointing

i still havent tried it out


Yeah, I think the problem is the game loop. My objective is to cap the frame rate of my game to 60 fps. v-sync does that but the monitor refresh rate must be 60hz, So, I can't rely on v-sync to cap my game fps. When I use my game loop to cap my fps to 60, the random jitter appears.

The jitter is mild but noticeable at some point.
Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #7 - Posted 2019-03-03 04:16:20 »

Lazy response on my part, as I am not taking the time to look at the code right now.

Some basic questions:

Do you employ blocking or synchronization? (Wondering if occasional deadlocks are hidden in your code somewhere.)

Do you always use loose-coupling when passing data between threads?

In my code example that I posted here, I used synchronized methods. I tried to use a non-synchronized method and the problem still persist.

I just use a single class in my example here and one anonymous class(the runnable class in SwingUtilities), I'm not sure if I implemented coupling here(since I'm not familiar with that kind of object oriented design).  
Offline philfrei
« Reply #8 - Posted 2019-03-04 04:28:02 »

Loose coupling mostly just means that classes are very hands-off in how they interact with each other. I was thinking, for example, that if you want to send a value to another class, and find that you have to use synchronization to do so, a better way would likely be to have a method in the target class that simply accepts the parameter and stores it in an instance variable and does nothing else. This should never block. Meanwhile, the target method which needs the value just grabs it from the instance variable, and thus is never required to pause due to synchronization code.

Another way to cap the rate on the game loop would be to use a util.Timer or ExecutorService, where you can set the repetition rate and dispense with the timing calculations. Each has two modes for repeating, depending on if you want some auto-correction on the timing.

If there is jitter, something is not right. Plenty of people have succeeded in producing smooth-running Java graphics.

music and music apps: http://adonax.com
Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #9 - Posted 2019-03-04 07:13:43 »

I tweaked my loop and the jitter is barely noticeable now when opengl pipeline is activated or my game is in full screen exclusive mode. When I disable opengl pipeline, the mild jitter will start to appear.

I'm not sure if anyone experiences it or it's just in my machine. There's a sample code in my question that anyone can try, let me know if you're experiencing the same issue.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Online VaTTeRGeR
« Reply #10 - Posted 2019-03-04 12:01:26 »

Quote
I'm not sure if anyone experiences it or it's just in my machine.
Tried the example code and it's a good 60fps experience after a few initial micro-stutters.

I cleaned the loop up a little:
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  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsDevice;
import java.awt.GraphicsConfiguration;
import java.awt.Color;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.awt.image.BufferStrategy;

import java.awt.geom.AffineTransform;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.imageio.ImageIO;

import java.io.File;

import java.util.Random;

public class ScreenTest extends JFrame {
   private static ScreenTest st;

   private Graphics2D g2dbf;
   //
   private Graphics g;

   private VolatileImage bgImage;

   private Canvas frameCanvas;
   // JFrame size
   private int frameWidth, frameHeight;

   // Identity transform
   private AffineTransform identityTrans;
   // Object transform
   private AffineTransform objTrans;
   //
   private BufferStrategy bufferS;

   // platform's graphics attributes...
   GraphicsEnvironment ge;
   GraphicsDevice gd;
   GraphicsConfiguration gc;

   public double posX, posY;
   private double velX, velY;
   public double recentPosY;

   int fps;
   private BufferedImage myImg;

   Random rand = new Random();

   ScreenTest() {
     
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
      // JFrame properties
      setTitle("Screen Test");
      setIgnoreRepaint(false);
      setResizable(true);
      setUndecorated(false);
     
     
      // Get platform's graphics attributes...
      ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      gd = ge.getDefaultScreenDevice();
      gc = gd.getDefaultConfiguration();

      // Set transform to identity transform
      identityTrans = new AffineTransform();
      // object transform
      objTrans = new AffineTransform();

      // Exclusive Full Screen Mode

      /*
       * gd.setFullScreenWindow( this ); if( gd.isDisplayChangeSupported() ) {
       * DisplayMode dm = gd.getDisplayMode(); frameWidth = dm.getWidth(); frameHeight
       * = dm.getHeight();
       *
       * gd.setDisplayMode( new DisplayMode( frameWidth, frameHeight,
       * dm.getBitDepth(), dm.getRefreshRate() )); }
       */


      // Simulated full screen mode
      // Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
      // frameWidth = (int)screenSize.getWidth();
      // frameHeight = (int)screenSize.getHeight();

      // Windowed Mode
      frameWidth = 640;
      frameHeight = 480;

      try {
         myImg = ImageIO.read(new File("img.png"));
      } catch (IOException e) {
         System.err.println(e);
      }

      posX = frameWidth / 2;
      posY = frameHeight / 2;

      velX = 0;
      velY = -6;
      recentPosY = posY;

      setSize(frameWidth, frameHeight);
      setVisible(true);
     
      createBufferStrategy(1);
      bufferS = getBufferStrategy();

      // back buffer image
      bgImage = gc.createCompatibleVolatileImage(frameWidth, frameHeight);
   }
   
   void update_Game() {
      updateImg();
   }

   void drawGraphics() {
      try {
         String str1 = String.format("Velocity X: %.2f", velX);
         String str2 = String.format("Velocity Y: %.2f", velY);

         g2dbf = bgImage.createGraphics();
         g2dbf.setTransform(identityTrans);
         // set background
         g2dbf.setColor(Color.BLACK);
         g2dbf.fillRect(0, 0, frameWidth, frameHeight);

         g2dbf.setTransform(identityTrans);
         objTrans.setToIdentity();
         objTrans.translate(posX, posY);
         g2dbf.drawImage(myImg, objTrans, null);

         // print some status information
         g2dbf.setColor(Color.WHITE);
         g2dbf.drawString("Image: " + Math.round(posX) + "," + Math.round(posY), 5, 40);
         g2dbf.drawString(str1, 5, 60);
         g2dbf.drawString(str2, 5, 80);

         g2dbf.drawString(String.format("FPS: %s", fps), 5, 140);

         g = bufferS.getDrawGraphics();
         g.drawImage(bgImage, 0, 0, null);

         if (!bufferS.contentsLost())
            bufferS.show();
         
         Toolkit.getDefaultToolkit().sync();

      } finally {
         if (g != null)
            g.dispose();
         if (g2dbf != null)
            g2dbf.dispose();
      }
   }

   synchronized void updateImg() {

      posX += velX;
      posY += velY;

      if (posX < -70)
         posX = frameWidth + 10;
      else if (posX > frameWidth + 10)
         posX = -10;

      if (posY < -10)
         posY = frameHeight + 10;
      else if (posY > frameHeight + 10)
         posY = -10;

   }

   public static void main(String[] args) throws InterruptedException, InvocationTargetException {
     
      System.setProperty("sun.java2d.opengl", "True");

      // System.setProperty("sun.java2d.d3d", "True");

      SwingUtilities.invokeAndWait(new Runnable() {
         @Override
         public void run() {

            st = new ScreenTest();
           
         }
      });

      long timeOfLastUpdate = System.nanoTime();
     
      final long FRAMES_PER_SECOND = 60;
      final long NANOS_PER_SECOND = 1_000_000_000;
     
      final long NANOS_PER_FRAME = NANOS_PER_SECOND / FRAMES_PER_SECOND;

     
      while (true) {

         long currentTime = System.nanoTime();
         
         long totalTime = currentTime - timeOfLastUpdate;

         if (totalTime >= NANOS_PER_FRAME) {
           
            // This will show 59 fps but that's because it's really 59.999... and then rounded down.
            st.fps = (int)( NANOS_PER_SECOND / totalTime );
           
            timeOfLastUpdate = currentTime;

            st.update_Game();
            st.drawGraphics();
         }

         Thread.yield();
      }
   }
}
Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #11 - Posted 2019-03-04 23:57:08 »

Thanks for checking out my sample code. just found out that if I reduce the createBufferStrategy() count to 1( createBufferStrategy(1); ), it makes the jitter more noticeable but the jitter frequency(how frequent jitter appears) is most likely the same. Although, createBufferStrategy(1); is working fine in the sample code program that I included in my question but it's not working well in my main game where images are abundant.

I feel satisfied with my loop right now. Maybe, the problem is the java2d pipeline itself.

I tried to increase the priority of my game loop thread to high priority assuming that the problem lay in the thread scheduler context switching and it seems like the jitter frequency is more less now.
Offline princec

« JGO Spiffy Duke »


Medals: 1067
Projects: 3
Exp: 20 years


Eh? Who? What? ... Me?


« Reply #12 - Posted 2019-03-05 00:15:26 »

This article just recently posted might be interesting:

https://medium.com/@tglaiel/how-to-make-your-game-run-at-60fps-24c61210fe75

Cas Smiley

Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #13 - Posted 2019-03-05 01:05:36 »

Thanks for the link dude. It looks like a great article. I'm going to read it later.
Offline princec

« JGO Spiffy Duke »


Medals: 1067
Projects: 3
Exp: 20 years


Eh? Who? What? ... Me?


« Reply #14 - Posted 2019-03-05 12:34:22 »

The 59.94Hz problem has plagued me for years too and I never realised what caused it. That simple 59...61 loop is awesome.

Cas Smiley

Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #15 - Posted 2019-03-11 11:19:11 »

So, I tried the game loops in this blog http://www.koonsolo.com/news/dewitters-gameloop/ and the loop that worked the best in my system was the variable time step(Game Speed dependent on Variable FPS). In the blog, it is said to avoid variable timestep, since my game doesn't contain too much physics I think I'm safe to use variable timestep.

So, now, let me lay out my problems about the other game loops in the blog.

My problem with lockstep/fixed time step(FPS dependent on Constant Game Speed) is that, when my game is not 60fps I see a flickering trailing image in my moving object, I suspect the trailing image is the previous image in previous frame, I'm using bufferstrategy, so double buffering problem has been ruled out. Also, my moving objects movement get choppy, jittery and jerky. What's causing this? In my knowledge, when fps is lower the game would just go slow. I suspect the problem is that the fps is not in sync with my monitor because when I put 60fps, I don't feel the jitter,jerks and choppiness but the stutter will appear.

About the last game loop in the blog,When I use the interpolation mechanism, my moving object get a sudden slow motion effect in a couple of seconds then will go back to normal. I don't know If I implemented it right, but I just followed the instruction in the blog.  

I also tried to use the fuzziness fix in this blog(great read btw) https://medium.com/@tglaiel/how-to-make-your-game-run-at-60fps-24c61210fe75 and the stutter didn't dissappear.

Any suggestion why I got jitter,jerks, choppiness and flickering trailing image in low fps and sudden slow motion when interpolation is implemented?

For now, I'm gonna use variable time step for my rendering and fixed timestep in my game physics.

Offline princec

« JGO Spiffy Duke »


Medals: 1067
Projects: 3
Exp: 20 years


Eh? Who? What? ... Me?


« Reply #16 - Posted 2019-03-12 11:06:35 »

There is one thing I'd say about variable time step, which is if your game is 2D, under no circumstances will variable time step feel nice. Lock it at 60, lock it at 30, whatever you do, make sure the logic is fixed and hope for vsync working.

Cas Smiley

Offline blue03corner

Senior Newbie


Exp: 2 years



« Reply #17 - Posted 2019-03-13 03:55:47 »

I really want to cap my fps. When I cap my fps to 60 i'm having a micro stutter, microstutters is fine with me, but I'm thinking "what if this game run in 75hz or 100+hz, will the stutter problem stay at microstutter or will it get worst?". I tried to cap my game fps to 30 and when I ran it on my 60hz monitor, I was having a stutter(not microstutter) and when I tried different fps lower than 60 like 50 or 25 I got a choppy movement and my moving object(especially the player object) got pretty shaky, It seemed like the the previous frame didn't completely cleared before the new frame drawn on the screen. This problem occurs even on a simpliest animation. Here is a sample code of two moving shape(circle and square)

http://pasted.co/993feeb5

I my system, the slow moving square has less shakes and the fast moving circle has more shakes, the stutter effect equivalently affects both objects at the same rate. I disable d3d and just use non-accelerated image(BufferedImage) in this example because I want my game to work(if possible) on a computers that don't support Direct3D or openGL. I tried to switch d3d and openGL on and off, tried to use volatileimage and switch vsync on and off and the result is similarly the same.

I want to use a fixed timestep with interpolation mechanism but I don't like the sudden slow motion effect that I get. Maybe my implementation of this loop was wrong that's why I get that sudden slow motion effect.

So, If those loops don't work on my system, then it might not work on other system too. Maybe the problem only lies on my system. I might release a demo version of my game to check if my game runs fine on various machine.
Offline philfrei
« Reply #18 - Posted 2019-03-13 21:55:32 »

For what it is worth, I took your posted code and rewrote the game loop to be controlled by a util.Timer. I'd did a bit of pruning of unused variables in my copy, so the code isn't exactly alike.

The jitter is quite similar, as far as I can tell.

The normal way of scheduling the TimerTask has even worse jitter than the "scheduleAtFixedRate" version. The main thing different between the two scheduling methods AFAIK is that the latter will attempt to "catch up" if it has fallen behind schedule. I assume this makes it more similar to the logic you have put in for your timing.

So, I think that this also confirms that the image rendering code is probably causing this, not the timing logic. But that puts this problem out of my expertise. When I render with Swing, I usually just use the double-buffering automatically provided by JPanel (when overriding paintComponent, and I don't recall getting this sort of jitter.

Nowadays, I use JavaFX which provides AnimationTimer. The simple code in this tutorial, a bouncing ball, has a couple serious jitters when starting out, but after a few seconds, proceeds with what you might consider an adequate degree of smoothness?

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  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
import java.lang.reflect.InvocationTargetException;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsDevice;
import java.awt.GraphicsConfiguration;
import java.awt.Color;
import java.awt.Toolkit;

import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class ScreenTest2
{
    private static ScreenTest2 st;
   
    private Graphics2D g2dbf;
    //
    private Graphics g;
   
    private BufferedImage bgImage;
   
    //JFrame size
    private int frameWidth,frameHeight;
   
    JFrame frame;
   
    //platform's graphics attributes...
   GraphicsEnvironment ge;
   GraphicsDevice gd;
   GraphicsConfiguration gc;    
   
    public double posX, posY;
    public double posY2,velY2;
    private double velX, velY;
    public double recentPosY;
   
    int fps;
   
    Random rand = new Random();
   
    ScreenTest2()
    {
        // Get platform's graphics attributes...
        ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        gd = ge.getDefaultScreenDevice();
        gc = gd.getDefaultConfiguration();
       
        //JFrame properties
        frame = new JFrame(gc);
        frame.setTitle("Screen Test2");
        frame.setIgnoreRepaint(true);
        frame.setResizable(false);
        frame.setUndecorated(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
        //Set transform to identity transform
       
        //Windowed Mode
        frameWidth = 640;
        frameHeight = 480;
        frame.setUndecorated(false);
               
        posX = frameWidth/2;
        posY = frameHeight/2;
        posY2 = posY;
 
        velX = 0;
        velY = -4;
        velY2 = -6;
        recentPosY = posY;
             
        frame.setSize(frameWidth,frameHeight);
        frame.setVisible(true);
               
        //back buffer image
        bgImage = gc.createCompatibleImage(frameWidth,frameHeight);
       
    }
   
    void update_Game()
    {
            updateImg();
    }
   
    void drawGraphics()
    {
        try
        {                
            g2dbf = bgImage.createGraphics();
            //set background
            g2dbf.setColor(Color.BLACK);
            g2dbf.fillRect(0,0,frameWidth,frameHeight);
                   
            g2dbf.setColor(Color.WHITE);
            g2dbf.fillRect((int)posX,(int)posY,50,50);
            g2dbf.fillOval((int)posX + 80,(int)posY2,50,50);
               
            //print some status information
            g2dbf.setColor(Color.WHITE);
            g2dbf.drawString("Image: " + Math.round(posX) + "," +
                  Math.round(posY) , 5, 40);
               
            g2dbf.drawString( String.format( "FPS: %s", fps ), 5, 60 );
               
            g = frame.getGraphics();
            g.drawImage(bgImage,0,0,null);
               
            Toolkit.getDefaultToolkit().sync();
        }
        finally
        {
                if(g != null) g.dispose();
                if(g2dbf != null) g2dbf.dispose();
        }
    }

   
    void updateImg()
    {        
        posX += velX;
        posY += velY;
        posY2 += velY2;
           
        if (posX < -70) posX = frameWidth + 10;
        else if (posX > frameWidth + 10) posX = -10;
               
        if (posY < -10) posY = frameHeight + 10;
        else if (posY > frameHeight + 10) posY = -10;
               
        if (posY2 < -10) posY2 = frameHeight + 10;
        else if (posY2 > frameHeight + 10) posY2 = -10;
    }
   
    public static void main(String[]args) throws InterruptedException,InvocationTargetException
    {
        //System.setProperty("sun.java2d.opengl", "True");
       
        //System.setProperty("sun.java2d.d3d", "True");
       
        System.setProperty("sun.java2d.d3d", "false");
       
        //System.setProperty("sun.java2d.trace","count");
       
        SwingUtilities.invokeAndWait(new Runnable(){
            @Override
            public void run()
            {
           st = new ScreenTest2();
           st.start();
            }
        });                  
    }
   
    private void start()
    {
        Timer timer = new Timer();
        ScreenTestTimerTask timerTask = new ScreenTestTimerTask();
       
        timer.scheduleAtFixedRate(timerTask, 100, 20);
        // jitter even worse with the normal update
//        timer.schedule(timerTask, 100, 20);
    }
   
    class ScreenTestTimerTask extends TimerTask
    {
      @Override
      public void run() {
           st.update_Game();
           st.drawGraphics();
      }
    }
}

music and music apps: http://adonax.com
Pages: [1]
  ignore  |  Print  
 
 

 
hadezbladez (1629 views)
2018-11-16 13:46:03

hadezbladez (644 views)
2018-11-16 13:41:33

hadezbladez (1628 views)
2018-11-16 13:35:35

hadezbladez (335 views)
2018-11-16 13:32:03

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

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

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

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

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

nelsongames (2951 views)
2018-04-24 18:14:32
Deployment and Packaging
by philfrei
2019-02-17 20:25:53

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