Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (497)
Games in Android Showcase (114)
games submitted by our members
Games in WIP (563)
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  
  [SOLVED] About "Thread.sleep" on my main loop  (Read 6379 times)
0 Members and 1 Guest are viewing this topic.
Offline co_Opernicus

Senior Newbie





« Posted 2012-04-15 20:11:14 »

Hi everyone!

  I'm new here and finally I've decided to sign in because I found this is the best place to learn java gaming techniques!! I hope I can help others too...

Well, here's my question:

  I'm building a "mini-engine" for a tribute game to "Monty" (from the C64 versions of "Monty on the run" and "Auf wiedersehen Monty"), and recently I solved a problem I had with some tear effects or glitches (I don't know how to call it...) in my animation code. Basically what happened was that in my computer everything went smooth but when I ran the tests on my laptop I noticed this issues. I'm using page flipping and in my "flip" method I had this:

public class MyGraphicsClass implements MyGraphicsInterface() {

  ... some methods here

   public void flip() {
      //screen.dispose();   <-- doesn't work. once disposed it has to be retrieved again?
      strategy.getDrawGraphics().dispose();
      strategy.show();
      
      try {
         Thread.sleep(10);
      } catch (Exception e) {}

   }
}

Now, instead of sleeping for 10 ms I try to make the loop take exactly 10ms so I changed it to this:

      try {
         Thread.sleep( game.getLastLoopMsecs() + 10 - HiresTimer.getTime() );
      } catch (Exception e) {}


Is this a good solution? Or is it better to keep a constant sleep time to let the OS "breathe" for a while? As I said, with a fix value is not so smooth in my laptop (Dell XPS)

Besides, I thought that as the game grows, every loop will take more time and the sleep time will be less and I don't know if that's ok or bad Smiley

Btw, for now I'm using Sun's Perf class in my HiresTimer (Eventually I'll have to use another system I guess... but atm I'm interested in getting the basics done)

Oh, and another thing: I'm using UPS instead of FPS, should I change to FPS for better performance or is UPS ok?

Offline Tim Spekler
« Reply #1 - Posted 2012-04-15 20:38:19 »

Hi !
As Thread.sleep(10) is independent from computer's performance, it is normal that there is a difference between two different computers. Instead you should do something like you do on the second example: get time before code execution, get time after and wait the appropriate among of time. This is the only way I know you can have a constant gameloop undependent from computer and size of code.
This is a way to do this (using FPS) (I hope there is no mistake  Smiley ).

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  
double FPS = 60 (if you want 60 FPS)
double secondsPerTick = 1/FPS;

public void gameloop(){
        // Game loop
       previousTime = System.nanoTime();
        while (/* Not closing window */) {
         long currentTime = System.nanoTime();
         long passedTime = currentTime - previousTime;
         previousTime = currentTime;
         unprocessedSeconds += passedTime / 1000000000.0;
         
         while (unprocessedSeconds > secondsPerTick){
             unprocessedSeconds -= secondsPerTick;
             tickCount++;
             if (tickCount % FPS == 0){
                System.out.println("FPS: " + frames);
                frames=0;
                previousTime+=1000;
             }
             // Execute code here
            do_code();
             frames++;
         }
      }
    }


For UPS, sorry but I'm not used to it...
The while process can be replace by a Thread.sleep (which is I think better)
Offline ra4king

JGO Kernel


Medals: 345
Projects: 3
Exp: 5 years


I'm the King!


« Reply #2 - Posted 2012-04-15 21:39:46 »

Hey co_Opernicus and welcome to JGO!

What is this "screen" object? Graphics2D? What exactly "doesn't work"? You're supposed to dispose of it after you're not done. Every time you call getDrawGraphics(), it gives you the Graphics2D object of another backbuffer, on that line "strategy.getDrawGraphics().dispose()", you're wasting a back buffer. Remove it!

As for sleeping, Thread.sleep is very inaccurate on Windows. You should use this sleeping method trick to make it more accurate. Put it at the beginning of your main method:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
if(System.getProperty("os.name").startsWith("Win")) {
   new Thread() {
      {
         setDaemon(true);
         start();
      }
     
      public void run() {
         while(true) {
            try {
               Thread.sleep(Long.MAX_VALUE);
            }
            catch(Exception exc) {}
         }
      }
   };
}


As for sleeping, the best way to sleep is through a busy loop. Here is some sample code of my 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  
public class MyGame implements Runnable {
    public static void main(String[] args) {
        new Thread(new MyGame()).start();
    }

    private final int WIDTH = 800, HEIGHT = 600;
    private final int FPS = 60;
   
    public void run() {
        JFrame frame = new JFrame("My game");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setIgnoreRepaint(true); //IMPORTANT, MUST INCLUDE!
       
        Canvas canvas = new Canvas();
        canvas.setPreferredSize(new Dimension(WIDTH,HEIGHT));
        canvas.setIgnoreRepaint(true); //IMPORTANT, MUST ALSO INCLUDE!
       frame.add(canvas);
       
        frame.pack();
        frame.setVisible(true);
       
        canvas.createBufferStrategy(2);
       
        BufferStrategy strategy = canvas.getBufferStrategy();

        long lastTime, lastFPS, frames = 0;
        lastTime = lastFPS = System.nanoTime();
       
        while(isRunning) {
            long deltaTime = System.nanoTime() - lastTime;
            lastTime += deltaTime;
           
            update(deltaTime);
           
            //Must setup these do-while loops like this according to BufferStrategy's Javadoc
           do{
                do{
                    Graphics2D g = (Graphics2D)strategy.getDrawGraphics();
                   
                    render(g);
                   
                    g.dispose();
                }while(strategy.contentsRestored());
               
                strategy.show();
            }while(strategy.contentsLost());
           
            //calculate the FPS
           frames++;
            if(System.nanoTime() - lastFPS >= 1e9) {
                System.out.println(frames);
                frames = 0;
                lastFPS += 1e9;
            }
           
            //Gets the remaining number of milliseconds left in the frame
           long sleepTime = Math.round((1e9/FPS - (System.nanoTime() - lastTime))/1e6);
            if(sleepTime <= 0)
                continue;
           
            //this sleeping method uses Thread.sleep(1) for the first 4/5 of the sleep loop, and Thread.yield() for the rest. This gives me an accuracy of about 3-4 microseconds
           long prev = System.nanoTime(), diff;
            while((diff = System.nanoTime() - prev) < sleepTime) {
                if(diff < sleepTime * 0.8)
                    try { Thread.sleep(1); } catch(Exception exc) {}
                else
                    Thread.yield();
            }
        }
    }
   
    public void update(long deltaTime) {
        //update logic
   }
   
    public void render(Graphics2D g) {
        //render your game
   }
}

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline co_Opernicus

Senior Newbie





« Reply #3 - Posted 2012-04-15 22:10:52 »

Hi there! Thanks for your reply!

What is this "screen" object? Graphics2D? What exactly "doesn't work"? You're supposed to dispose of it after you're not done. Every time you call getDrawGraphics(), it gives you the Graphics2D object of another backbuffer, on that line "strategy.getDrawGraphics().dispose()", you're wasting a back buffer. Remove it!

  Sorry, my bad... that line was from an earlier version of the game, since this is the first time I'm using Java2D I didn't understand well why I had to call "dispose" so I did some tests... But anyway, I still don't get it. If I remove that line, when do I have to call dispose? Because now I've just commented it and everything works fine even if a don't call it anywhere else o_O... Does it mean that "show" takes care of swapping buffers automatically?

For the rest, thanks a lot for the tips on sleeping!! I'll try them
Offline ra4king

JGO Kernel


Medals: 345
Projects: 3
Exp: 5 years


I'm the King!


« Reply #4 - Posted 2012-04-15 22:38:35 »

You're disposing of the resources that Graphics2D uses. While it's not absolutely necessary to dispose it, it wouldn't hurt to add that line in.

And yes, show() takes care of swapping the buffers.

EDIT: Btw, take care to notice the double do-while loop pairs around the call to the render method. Those are necessary.

Offline co_Opernicus

Senior Newbie





« Reply #5 - Posted 2012-04-15 22:58:32 »

You're disposing of the resources that Graphics2D uses. While it's not absolutely necessary to dispose it, it wouldn't hurt to add that line in.

The problem with my "screen.dispose()" is that I initialized only once, when creating the instance, instead of doing it inside the loop as you do.
 
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
 do{
     do{
         Graphics2D g = (Graphics2D)strategy.getDrawGraphics();                
         render(g);
                   
         g.dispose();
     }while(strategy.contentsRestored());
               
  strategy.show();
  }while(strategy.contentsLost());


   That's why it didn't work and I commented it... and that's why I introduced "strategy.getDrawGraphics().dispose()" before doing strategy.show() without noticing that it has to be called before rendering. I'll have to change some things in my design because right now my Graphics class is separated from the rendering and "strategy" is out of the scope of the rendering loop

Anyway, I think it's time for some code refactoring... hehe  

Again, thanks for your replies, they're really helpful
Offline co_Opernicus

Senior Newbie





« Reply #6 - Posted 2012-04-15 22:59:41 »

Thanks Tim! I'll check this when implementing the FPS control part
Offline ra4king

JGO Kernel


Medals: 345
Projects: 3
Exp: 5 years


I'm the King!


« Reply #7 - Posted 2012-04-15 23:08:32 »

Concerning Tim's FPS count, he only prints it when it reaches 60, therefore not actually calculating how many frames per second have been rendered.

Offline ReBirth
« Reply #8 - Posted 2012-04-15 23:42:16 »

Without any attempt t offend, I prefer ra4king's code. I used like Tim's one half year ago and yes, it worked. However that double loop while make better performance. Since then I'm following him Wink

Offline Tim Spekler
« Reply #9 - Posted 2012-04-16 17:06:54 »

Sorry, you're right ra4king, my FPS will always display 60 FPS  Tongue
I forgot to add do_code and frame++ again behind the while loop (then it should be better !)
But anyway, I'm sure ra4king proposition is better. I'll take a look at it  Wink
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

BurntPizza (15 views)
2014-09-19 03:14:18

Dwinin (33 views)
2014-09-12 09:08:26

Norakomi (58 views)
2014-09-10 13:57:51

TehJavaDev (81 views)
2014-09-10 06:39:09

Tekkerue (40 views)
2014-09-09 02:24:56

mitcheeb (63 views)
2014-09-08 06:06:29

BurntPizza (46 views)
2014-09-07 01:13:42

Longarmx (33 views)
2014-09-07 01:12:14

Longarmx (38 views)
2014-09-07 01:11:22

Longarmx (36 views)
2014-09-07 01:10:19
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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!