Game Loops! - Java-Gaming

Embed Size (px)

Citation preview

  • 8/11/2019 Game Loops! - Java-Gaming

    1/14

    Java-Gaming.org

    Featured games (81) games approved by the League of Dukes

    Games in Showcase (499) Games in Android Showcase (118)

    games submitted by our members

    Games in WIP (568) games currently in development

    News : Read the Java Gaming Resources, or peek at the official Java tutorials Search

    HOME HELP SEARCH LOGIN REGISTER

    Java-Gaming.org > Game Development > Articles & tutorials > Game loops!

    Pages: [ 1 ] 2 3 IGNORE | PRINT

    Game loops!

    There are a lot of different sorts of game loops you can use. In general, you can think of a game loop ashaving 3 different pieces:

    1) Some sort of, well... loop. From timer calls to while loops to recursive function calls. (the loop )2) A way to delay the length of each loop iteration so you get a frame rate you like. (the timingmechanism )3) A way to make the game speed remain independent of the speed of the loop. (the interpolation )

    Bad LoopsLet's start with some pretty ugly ones I've seen that you definitely shouldn't do:

    1

    23456789

    public void gameLoop ()

    { while ( true ) //the loop { doGameUpdates (); render (); for ( int i = 0; i < 1000000 ; i ++) ; //the timing mechanism }}

    OMG so bad. Don't ever do that. I've literally seen it in people's games though. Aside from maxing out your processor, that's going to be a completely varying length of time for each machine and even might havevariation on your machine. Bad bad bad. You can also see that this is missing a method of interpolation, but

    there's no way to provide that when it doesn't measure time in any way!

    123456789

    public void gameLoop (){ while ( true ) //the loop { doGameUpdates (); render (); Thread . sleep ( 1); //the timing mechanism }}

    This is better, but still not great. Thread.sleep is not always going to be accurate, plus if you've got anyother threads hogging processor it won't necessarily allow your thread to resume in time. From the javadocs:

    Quote

    Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision andaccuracy of system timers and schedulers.

    Also, this is missing interpolation. That's because this sort of loop assumes that Thread.sleep() is actuallygoing to be 100% accurate, but we know it isn't.

    Another issue that we see with both of these loops is the self-defined infinite loop that is "while(true)." We

    know that this loop can never ever end. It's impossible. I will absolutely advise against doing this, ever. It willonly cause problems. Even though, conceptually, we don't want this loop ever to end, it's a good idea to popa boolean in there so you at least have the option if killing the loop.

    1 private boolean isRunning ;

    Eli Delventhal

    http://www.java-gaming.org/index.php?PHPSESSID=fcbc0i3tls8t8qo0sfbvos8n64&action=profile;u=6083
  • 8/11/2019 Game Loops! - Java-Gaming

    2/14

    23456789

    1011

    public void gameLoop (){ while ( isRunning ) //the loop { doGameUpdates (); render (); Thread . sleep ( 1); //the timing mechanism }}

    Great, now we can set isRunning to false whenever we want to stop the game loop, or pause the game, or anything like that.

    Here's a crazy recursive way of doing the same loop:

    123456789

    101112

    private boolean isRunning ;

    public void gameLoop (){ doGameUpdates (); render (); Thread . sleep ( 1); //the timing mechanism if ( isRunning ) { gameLoop (); //the loop }}

    I don't know why'd you ever do that, but it's another thing I've seen so I've included it for completion's sake. Aside from being not obvious, I think this will eventually cause a stack overflow (someone correct me if I'mwrong).

    Decent loops, but I still wouldn't use them

    I think I've seen the approach of using either java.util.Timer or javax.swing.Timer more often than any other approach (at least in amateur projects). This is nice because it saves you from having to deal with any partof the loop yourself, and it's more or less accurate. The reason for this is that neither of the two Timer classes are intended to be used as heavy-lifting tasks. java.util.Timer specifically says "Timer tasks shouldcomplete quickly. If a timer task takes excessive time to complete, it "hogs" the timer's task executionthread" in the Java docs. Even better, javax.swing.Timer is meant to be used with Swing (go figure), so it isnot at all a reliable timing solution. It fires all events on the EDT (event-dispatching-thread), which is usedfor all Swing events. So, your action might be fired after a lot of other things, thereby resulting in a veryunpredictable timing solution.

    Still, avoiding the overhead of dealing with your own timing system can be nice.

    java.util.Timer:

    123456789

    10111213141516171819202122

    private java . util . Timer timer ;private boolean isRunning ;

    public void gameLoop (){ timer = new Timer (); timer . schedule ( new LoopyStuff (), 0, 1000 / 60); //new timer at 60 fps, the timing mec}

    private class LoopyStuff extends java . util . TimerTask{ public void run () //this becomes the loop { doGameUpdates (); render ();

    if (! isRunning ) { timer . cancel (); } }}

    I won't bother laying out javax.swing.Timer, because it is totally suck. Don't use it!

    Good game loopsSo, in all those examples, we've got issues with the reliability of timers. If your game logic is only updatingat 30 times per second, how do you draw anything at 60 fps? Similarly, if your fps is down to 10, how do youkeep the updates at 30? Or, if your game is updating at 70 times per second one frame and 10 times per second another frame, how do you ensure that the game speed is consistent for players?

  • 8/11/2019 Game Loops! - Java-Gaming

    3/14

    Remember that third component of a game loop that we haven't used yet? That's right, we need to useinterpolation.

    In terms of your loop, you have two options:- Variable timestep- Fixed timestep

    Which one you use depends on personal preference and also what sort of things you are doing in your loops.

    Variable timestep loops are great because the game will seem consistent regardless of how fast theplayer's computer is. allowing you to potentially cater to lots of different types of machines. They also oftenallow you to update the game logic with very high granularity, that can make a much better experience incertain games. Things are drawn as they change position, so there is no graphical latency whatsoever.

    Fixed timestep loops are great because you know that every single timestep will take up the exact samelength of time. This means you will always have consistent gameplay, regardless of how fast a machine is.This works wonderfully for math-heavy games, like physics operations, and is also my loop of choice for networked games so that you know packets are going and coming at a generally constant speed. You alsocan keep the game logic running at a very low rate while the frame rate can still get extremely high, albeitwith one frame of latency.

    You can't really use variable timestep well in physics simulations, and fixed timestep fails in situationswhere your game can be interrupted or on machines that are too slow to hit your fixed rate.

    Here's where the interpolation comes in:- If you are using a variable stepped loop, then you need to update the game different amounts dependingon how long recent updates took. You will use a delta value to do this, which you multiply times every singlevalue that updates based on time (think of things like velocity, position, attack rate, and the like). A delta of 1.0 means that your loop took as long as you normally expect (an "optimal" amount of time), where as adelta of < 1 means that the loop is going faster than optimal, and a delta of > 1 means that it's slower.- If you are using a fixed timestep, then you have three options: you can either have a very high fixedupdate (which lowers the number of machines that can reliably run your game), you can have a very lowframe rate (if the positions of your characters are only updating 20 times per second, then no matter howfast the rendering is going it's only going to render those 20 frames), or you can interpolate the most recentupdate to the current one over each render. That sounds confusing, but it's not hard to implement, and it

    gives you yummy butter smoothness!

    Both obviously have some caveats you're going to need to worry about. With the former, you need tomultiply every single time-based updated values by the delta. This is a pain in the butt and it's pretty easy toforget to multiply by the delta sometimes, but it's reliable and it works. In the latter, you've got to multiply alltime-based rendered values by the interpolation amount. Also a pain in the butt!

    Personally, I usually use a fixed timestep loop, because it's generally less to think about. My logic is almostalways much more complicated than my rendering, and you can usually abstract out the interpolation sothat you don't have to worry about it more than once. However, I'd use whatever makes sense to you!

    Without further ado, here are implementations:Variable timestep (credit goes to Kevin Glass on his site , with heavy changes)

    123456789

    101112131415161718192021222324252627

    282930313233

    public void gameLoop (){ long lastLoopTime = System . nanoTime (); final int TARGET_FPS = 60; final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;

    // keep looping round til the game ends while ( gameRunning ) { // 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 now = System . nanoTime (); 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; }

    // update the game logic doGameUpdates ( delta );

  • 8/11/2019 Game Loops! - Java-Gaming

    4/14

    343536373839404142434445464748495051525354555657

    5859606162636465

    // draw everyting render ();

    // we want each frame to take 10 milliseconds, to do this // we've recorded when we started the frame. We add 10 milliseconds // to this and then factor in the current time to give

    // us our final value to wait for // remember this is in ms, whereas our lastLoopTime etc. vars are in ns. try {Thread . sleep ( ( lastLoopTime - System . nanoTime () + OPTIMAL_TIME)/ 1000000 )}; }}

    private void doGameUpdates ( double delta ){ for ( int i = 0; i < stuff . size (); i ++) { // all time-related values must be multiplied by delta! Stuff s = stuff . get ( i ); s . velocity += Gravity . VELOCITY * delta ; s . position += s . velocity * delta ;

    // stuff that isn't time-related doesn't care about delta... if ( s . velocity >= 1000 ) {

    s . color = Color . RED; } else { s . color = Color . BLUE; } }}

    Fixed timestep (credit goes to me, this includes an example with a ball bouncing around so that you canclearly see how interpolation works)

    123456789

    1011121314151617181920212223242526

    27282930313233343536373839404142434445

    import javax . swing .*;import java . awt .*;import java . awt . event .*;

    public class GameLoopTest extends JFrame implements ActionListener{ private GamePanel gamePanel = new GamePanel (); private JButton startButton = new JButton ( "Start" ); private JButton quitButton = new JButton ( "Quit" ); private JButton pauseButton = new JButton ( "Pause" ); private boolean running = false ; private boolean paused = false ; private int fps = 60; private int frameCount = 0;

    public GameLoopTest () { super ( "Fixed Timestep Game Loop Test" ); Container cp = getContentPane (); cp . setLayout ( new BorderLayout ()); JPanel p = new JPanel (); p. setLayout ( new GridLayout ( 1, 2)); p. add ( startButton ); p. add ( pauseButton ); p. add ( quitButton ); cp . add ( gamePanel , BorderLayout . CENTER);

    cp . add ( p, BorderLayout . SOUTH); setSize ( 500 , 500);

    startButton . addActionListener ( this ); quitButton . addActionListener ( this ); pauseButton . addActionListener ( this ); }

    public static void main ( String [] args ) { GameLoopTest glt = new GameLoopTest (); glt . setVisible ( true ); }

    public void actionPerformed ( ActionEvent e) { Object s = e . getSource (); if ( s == startButton ) {

  • 8/11/2019 Game Loops! - Java-Gaming

    5/14

    464748495051525354555657585960616263646566676869

    70717273747576777879808182

    8384858687888990919293949596979899

    100101102103104105106107108109

    110111112113114115116117118119120121122123124125126127128

    running = ! running ; if ( running ) { startButton . setText ( "Stop" ); runGameLoop (); } else { startButton . setText ( "Start" ); } } else if ( s == pauseButton ) { paused = ! paused ; if ( paused ) { pauseButton . setText ( "Unpause" ); } else { pauseButton . setText ( "Pause" ); } } else if ( s == quitButton )

    { System . exit ( 0); } }

    //Starts a new thread and runs the game loop in it. public void runGameLoop () { Thread loop = new Thread () { public void run () { gameLoop ();

    } }; loop . start (); }

    //Only run this in another Thread! private void gameLoop () { //This value would probably be stored elsewhere. final double GAME_HERTZ = 30.0 ; //Calculate how many ns each frame should take for our target game hertz. final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ; //At the very most we will update the game this many times before a new render. //If you're worried about visual hitches more than perfect timing, set this to 1. final int MAX_UPDATES_BEFORE_RENDER = 5; //We will need the last update time. double lastUpdateTime = System . nanoTime (); //Store the last time we rendered. double lastRenderTime = System . nanoTime ();

    //If we are able to get as high as this FPS, don't render again. final double TARGET_FPS = 60; final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;

    //Simple way of finding FPS. int lastSecondTime = ( int ) ( lastUpdateTime / 1000000000 );

    while ( running ) { double now = System . nanoTime (); int updateCount = 0;

    if (! paused ) { //Do as many game updates as we need to, potentially playing catchup. while ( now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDA { updateGame (); lastUpdateTime += TIME_BETWEEN_UPDATES; updateCount ++; }

    //If for some reason an update takes forever, we don't want to do an insane//If you were doing some sort of game that needed to keep EXACT time, you woif ( now - lastUpdateTime > TIME_BETWEEN_UPDATES)

    {

  • 8/11/2019 Game Loops! - Java-Gaming

    6/14

    129130131132133134135136137138139140141142143144145146147148149150151152

    153154155156157158159160161162163164165

    166167168169170171172173174175176177178179180181182183184185186187188189190191192

    193194195196197198199200201202203204205206207208209210211

    lastUpdateTime = now - TIME_BETWEEN_UPDATES; }

    //Render. To do so, we need to calculate interpolation for a smooth render. float interpolation = Math. min( 1.0f , ( float ) (( now - lastUpdateTime ) / TIME_ drawGame( interpolation ); lastRenderTime = now;

    //Update the frames we got. int thisSecond = ( int ) ( lastUpdateTime / 1000000000 ); if ( thisSecond > lastSecondTime ) { System . out . println ( "NEW SECOND " + thisSecond + " " + frameCount ); fps = frameCount ; frameCount = 0; lastSecondTime = thisSecond ; }

    //Yield until it has been at least the target time between renders. This savwhile ( now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpda {

    Thread . yield ();

    //This stops the app from consuming all your CPU. It makes this slightly

    //You can remove this line and it will still work (better), your CPU just//FYI on some OS's this can cause pretty bad stuttering. Scroll down andtry { Thread . sleep ( 1);} catch ( Exception e) {}

    now = System . nanoTime (); } } } }

    private void updateGame () { gamePanel . update ();

    } private void drawGame( float interpolation )

    { gamePanel . setInterpolation ( interpolation ); gamePanel . repaint (); }

    private class GamePanel extends JPanel { float interpolation ; float ballX , ballY , lastBallX , lastBallY ; int ballWidth , ballHeight ; float ballXVel , ballYVel ; float ballSpeed ;

    int lastDrawX , lastDrawY ;

    public GamePanel () { ballX = lastBallX = 100 ; ballY = lastBallY = 100 ; ballWidth = 25; ballHeight = 25; ballSpeed = 25; ballXVel = ( float ) Math. random () * ballSpeed *2 - ballSpeed ; ballYVel = ( float ) Math. random () * ballSpeed *2 - ballSpeed ;

    }

    public void setInterpolation ( float interp ) { interpolation = interp ; }

    public void update () { lastBallX = ballX ; lastBallY = ballY ;

    ballX += ballXVel ; ballY += ballYVel ;

    if ( ballX + ballWidth / 2 >= getWidth ()) { ballXVel *= - 1; ballX = getWidth () - ballWidth / 2;

  • 8/11/2019 Game Loops! - Java-Gaming

    7/14

    212213214215216217218219220221222223224225226227228229230231232233234235

    236237238239240241242243244245246247248

    249250251252253254255256257258259260261262263264265266267268269270271272273274275

    276277278279280281282283284285286287288289290291292293294

    ballYVel = ( float ) Math. random () * ballSpeed *2 - ballSpeed ; } else if ( ballX - ballWidth / 2 = getHeight ()) { ballYVel *= - 1; ballY = getHeight () - ballHeight / 2; ballXVel = ( float ) Math. random () * ballSpeed *2 - ballSpeed ; } else if ( ballY - ballHeight / 2 = gamePanel . getWidth ()) { xVelocity *= - 1; x = gamePanel . getWidth () - width / 2; yVelocity = ( float ) Math. random () * speed *2 - speed ; } else if ( x - width / 2 = gamePanel . getHeight ())

  • 8/11/2019 Game Loops! - Java-Gaming

    8/14

    295296297298299300301302303304305306307308309310311312

    { yVelocity *= - 1; y = gamePanel . getHeight () - height / 2; xVelocity = ( float ) Math. random () * speed *2 - speed ; } else if ( y - height / 2

  • 8/11/2019 Game Loops! - Java-Gaming

    9/14

    - It's down to personal preference of course, but for variable time steps I prefer to pass the elapsed time in seconds (a doubleobviously) to the update function, rather than a 'delta' relative to a nominal time step (1/60th of second in your code). Myreasons: If you've got a game object that works on a timer (e.g., a bomb that detonates after five seconds) then it's nice if thecode doesn't have to keep converting back and forth between seconds and nominal time steps. Also, you don't have to rewrite lotsof code if you want to change the value of the nominal time step. As I say, personal preference, but I thought I'd make the casefor the alternative approach.

    - It took me a while to realise that you're talking about fixed time steps with respect to the update function only. I would arguethat a game loop that uses fixed time steps for both updating and rendering is the most newbie-friendly approach. Any chance of including an example of that in the tutorial? I know that getting perfectly smooth results that way can be difficult, but I think it'sbetter for beginners than trying to struggle with either variable time steps in the update function or interpolation in the renderfunction.

    - How about making all examples implement the same bouncing ball demo? That would let people compare the differentapproaches more easily. It would be even better if there were links to the examples running as applets/webstart so people have

    evidence that the code works as advertised on their machine.- (I'm not entirely sure this is a good idea, but if in the example update functions you added long delays (sleeps) at randomintervals, that would demonstrate how good the game loop is at remaining smooth in adverse conditions.)

    - Is there a reason for not using BufferStrategy in the examples?

    - Gravity.ACCELERATION not Gravity.VELOCITY in the variable time step example.

    - I think it's important in a variable time step loop to impose a maximum on the time step value passed to the update function(1/10th of a second, say). If the game hits a delay longer than the maximum then it's not going to feel smooth whatever you do,and I bet that a lot of update f unctions will behave strangely when the time step is unexpectedly long (bullets will jump straightover aliens, Mario will fall through the platform below him, etc.).

    - In the variable time step example, the argument to Thread.sleep() can go negative. I don't know how the function reacts to that(although presumably it does nothing).

    - The Ball class in the fixed time step example is never used.

    - In the fixed time step example, I don't think it's possible for the following clause to be executed ( now is always greater than

    lastUpdateTime ).

    1234

    if ( lastUpdateTime - now > TIME_BETWEEN_UPDATES){ lastUpdateTime = now - TIME_BETWEEN_UPDATES;}

    - I've never tried using interpolation in the render function, but I can't help feeling that it might introduce some strange glitchesunless you're very careful. For instance, if you're interpolating between the player being alive in one position, and dead in anotherposition, there's a danger that the player's corpse might appear to jump around the screen briefly. Is this a problem in yourexperience?

    Cheers,Simon

    ra4kingAdding on Simon's (dishmoth's) post, you are depending on two threads for game loop and drawing which could cause sometrouble since it might be in the middle of redrawing when it sets a new interpolation value and thus your redraw is completelythrown off. Maybe synchronized the methods or just use 1 thread for logic and render?

    Eli DelventhalThanks for all those comments, Simon! I'll definitely put some of them in as I get the time, especially putting the ball example infor other examples. I agree that's a great idea, especially having applets bundled with them too.

    @ra4king: Which example are you referring to? I don't think any of them have a separate thread for rendering and updating.

    ra4kingYou call repaint(), which means you use passive rendering, depending on the EDT (Event Dispatching Thread) to callpaintComponent(). Your game loop, meanwhile is in another thread that you start inside runGameLoop(). It is recommended, formore professional games that use Java2D, to use BufferStrategy and have game logic and rendering in the same thread.

    EDIT: Oh and I am talking about your Bouncing Ball example

    Eli DelventhalQuote from: ra4king on May 15, 2011

    You call repaint(), which means you use passive rendering, depending on the EDT (Event Dispatching Thread) to callpaintComponent(). Your game loop, meanwhile is in another thread that you start inside runGameLoop(). It is recommended, formore professional games that use Java2D, to use BufferStrategy and have game logic and rendering in the same thread.

    EDIT: Oh and I am talking about your Bouncing Ball example

    Ah yes, this is true. I merely did that because it was quick to implement and I figured the most newbies would understand thepaintComponent/repaint model. I had to include something actually happening to illustrate how to do interpolation with your

    drawing. But who am I kidding, people will likely just copy/paste the code, so I'll put in a better method at some point.

    ra4king Quote from: Eli Delventhal on May 16, 2011

    Quote from: ra4king on May 15, 2011

    You call repaint(), which means you use passive rendering, depending on the EDT (Event Dispatching Thread) to callpaintComponent(). Your game loop, meanwhile is in another thread that you start inside runGameLoop(). It is recommended, formore professional games that use Java2D, to use BufferStrategy and have game logic and rendering in the same thread.

    EDIT: Oh and I am talking about y our Bouncing Ball example

    Ah yes, this is true. I merely did that because it was quick to implement and I figured the most newbies would understand thepaintComponent/repaint model. I had to include something actually happening to illustrate how to do interpolation with your

    drawing. But who am I kidding, people will likely just copy/paste the code, so I'll put in a better method at some point.

    Yes I was fearing the copy/paste part

    philfreiI've been doing some testing over at this link, a complaint about jitteriness in a game loop:http://www.java-gaming.org/topics/slight-jerkiness/24311/view.html

    I wish to toss some comments and challenges to Eli!

    1) for using a util.Timer, consider using the form that tries to keep the scheduled repeats based on the starting time, as in:

  • 8/11/2019 Game Loops! - Java-Gaming

    10/14

    1 myTimer . scheduleAtFixedRate ( new GameLoopIteration (), 0, 20);

    The usual method, "myTimer.shedule(new GameLoopIteration(), 0, 20);" bases the next iteration time on the completion of theprevious iteration, and thus can drift. "ScheduleAtFixedRate" will still be stuck with using the OS time granularity, but it willalternate between the enclosing trigger times. Example: timer repeat request: 20msec. System clock granularity: 15msec. Thetimer will trigger either at intervals of 15 or 30, depending upon whether it is running ahead or behind.

    2) Sleep is still dependent upon the constraints of the system, even if you have "Thread.sleep(1)" as you do in your "good" gameloops. According to my tests, it seems there is no guarantee that the sleep will not occasionally last a full increment of the OStiming system. As I show (reply #15 in the above quoted thread) in some nano measurements of sleep(1), the sleep occasionallylasts 15msec. (The 15msec granularity I get is part of Windows XP OS.)

    I have an idea that I am going to try as an experiment, to schedule absolute times via a util.Timer. Let's see if the initiation of ascheduled TimerTask, given an absolute time, is implemented to be more accurate than the OS constraints. It will be like laying

    train tracks just ahead of t he moving train. I did it before with some sound structures.EDIT: nope, laying out absolute times for the timer tasks at 20msec intervals still resulted in iterations going back and forth

    between 15.5 and 31 msec iterations.

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

    Eli DelventhalThanks a lot for the comments and challenge. It is indeed a challenge to get it absolutely perfect, and I agree that using Timer isnot the best way to go. Instead, I would use yield() and one of the last two loops I posted, and maybe put in Thread.sleep(1) if you're worried about using too much processor.

    ra4kingQuote from: philfrei on July 27, 2011

    2) Sleep is still dependent upon the constraints of the system, even if you have "Thread.sleep(1)" as you do in your "good" gameloops. According to my tests, it seems there is no guarantee that the sleep will not occasionally last a full increment of the OStiming system. As I show (reply #15 in the above quoted thread) in some nano measurements of sleep(1), the sleep occasionallylasts 15msec. (The 15msec granularity I get is part of Windows XP OS.)

    Try having a thread that sleeps forever like this:

    1

    2345678

    new Thread () {

    public void run () { try { Thread . sleep ( Long. MAX_VALUE); } catch ( Exception exc ) {} }}. start ();

    That forces Java to use the high resolution timer, which makes sleep(1) MUCH more accurate.

    philfreiPerils of running a conversation in two places...the code shown by avm1979 tested to work perfectly for me. I put some test resultson the other thread...mentioned prior.

    @ra4king, your example worked perfectly, as well! Every sleep measured out to within a single msec. That's good enough for me.So, appreciations to you both.

    Also, after reading your reply, I did a search on "high resolution Timer java" and found this thread:http://www.java-gaming.org/index.php?topic=1071.0So, it seems sun.misc.Perf is still around. I found it in the basic library: rt.jar. I'm going to test that next. Also, came across thesuggestion that a ScheduledThreadPoolExecutor is an improved Timer and works. More stuff to test!

    P.S., avm1979's idea of making the background sleeper a daemon makes good sense, and if you aren't already doing this, consideradding that fine point. Example on the link below, post #16.

  • 8/11/2019 Game Loops! - Java-Gaming

    11/14

    http://www.java-gaming.org/topics/slight-jerkiness/24311/view.html

    ra4kingAh yes I have seen both posts.

    And what's a daemon and how would it help? O_o

    cylabQuote from: ra4king on July 28, 2011

    Ah yes I have seen both posts.

    And what's a daemon and how would it help? O_o

    http://www.jguru.com/faq/view.jsp?EID=43724

    ra4kingQuote from: cylab on July 28, 2011

    Quote from: ra4king on July 28, 2011

    Ah yes I have seen both posts.

    And what's a daemon and how would it help? O_o

    http://www.jguru.com/faq/view.jsp?EID=43724

    Ah makes sense thanks!

    Rejechted I have a bit of an issue when your game consists of more than just bodies moving at fixed (non changing) velocities.

    Say you introduce acceleration. Suddenly, linearly interpolating the drawx and drawy doesn't make any sense, since the velocity of your ball has changed between frames A and B.

    Additionally, what happens when you start to introduce shifts in color over time? Say we have an effect that is placed on an entitythat changes its texture color from white to red slowly over 5 seconds. Obviously its more noticeable when things are jitteryposition-wise, but in a truly interpolated fixed timestep system, is the only way to achieve perfect accuracy t o interpolate each andevery thing that can undergo a change before it is drawn?

    philfreiYou have to keep in mind that human senses are limited. Certain mathematical niceties are simply too small to discriminate. So, ina lot of situations linear interpolation is good enough, especially if you have a healthy frame rate going.

    [Added] There is a minimum color distance that is observable as a discontinuity. This minimum distance might be numericallydifferent depending upon where in the color space you are located. Often the relationship between perception and the numbersused to traverse a space is logarithmic rather than linear.

    Also, there are a number of "color spaces" to choose from besides RGB for color traversal, which has to be considered.

    In any case, the amount of change between frames should to be below the amount that triggers a perceptual discontinuity, or youwill get some sort of artifact. So you have to be responsible for knowing what that minimum is and providing some sort of throttleor max delta function (if you want to avoid the discontinuities).

    RejechtedRight. So I guess by the sound of that, the interpolation of positions of bodies/images/particle emitters in the game, as well asangles, is enough for human detection. I guess this makes sense, especially when you consider you're talking about sixtieths of asecond.

    What about interpolating the position of the v iewport for a GL Lookat call?

    Eli DelventhalIt doesn't matter what you do as long as you have one frame of latency. You interpolate between whatever the last displayed

    values were (position/color/scale) and the current ones. Whatever caused the changes in positions doesn't matter because you'reonly ever directly representing position itself.

    Make sense?

    ChromanoidAt least the fixed timestep loop has some jittering related to the waiting at the end. It would be good to mention this in the OPsince people are using this code: http://forums.tigsource.com/index.php?topic=22054.0

    ChromanoidI made the following gameloop. It doesn't stutter on my system. However it doesn't sleep as much as other ones.

    1234

    56789

    1011121314151617181920212223242526272829303132

    import java . awt . BasicStroke ;import java . awt . Color ;import java . awt . Graphics2D ;import java . awt . Toolkit ;

    import java . awt . event . MouseAdapter ;import java . awt . event . MouseEvent ;import java . awt . image . BufferStrategy ;

    public class Game extends javax . swing . JFrame {

    private static final long serialVersionUID = 1L; /* difference between time of update and world step time */ float localTime = 0f ;

    /** Creates new form Game */ public Game() { setDefaultCloseOperation ( javax . swing . WindowConstants . EXIT_ON_CLOSE); this . setSize ( 800 , 600); }

    /** * Starts the game loop in a new Thread. * @param fixedTimeStep * @param maxSubSteps maximum steps that should be processed to catch up with real

    */ public final void start ( final float fixedTimeStep , final int maxSubSteps ) { this . createBufferStrategy ( 2); init (); new Thread () {

    { setDaemon ( true );

  • 8/11/2019 Game Loops! - Java-Gaming

    12/14

    333435363738394041424344454647

    48495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899

    100101102103104105106

    107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154

    }

    @Override public void run () { long start = System . nanoTime (); while ( true ) { long now = System . nanoTime (); float elapsed = ( now - start ) / 1000000000f ; start = now; internalUpdateWithFixedTimeStep ( elapsed , maxSubSteps , fixedTimeStep internalUpdateGraphicsInterpolated (); if ( 1000000000 * fixedTimeStep - ( System . nanoTime () - start ) > 1000

    try { Thread . sleep ( 0, 999999 ); } catch ( InterruptedException ex ) {

    } } } } }. start (); }

    /** * Updates game state if possible and sets localTime for interpolation. * @param elapsedSeconds * @param maxSubSteps * @param fixedTimeStep

    */ private void internalUpdateWithFixedTimeStep ( float elapsedSeconds , int maxSubSteps ,

    int numSubSteps = 0; if ( maxSubSteps != 0) { // fixed timestep with interpolation localTime += elapsedSeconds ; if ( localTime >= fixedTimeStep ) { numSubSteps = ( int ) ( localTime / fixedTimeStep ); localTime -= numSubSteps * fixedTimeStep ; } } if ( numSubSteps != 0) { // clamp the number of substeps, to prevent simulation grinding spiralling

    int clampedSubSteps = ( numSubSteps > maxSubSteps ) ? maxSubSteps : numSubSte for ( int i = 0; i < clampedSubSteps ; i ++) { update ( fixedTimeStep ); } } }

    /** * Calls render with Graphics2D context and takes care of double buffering. */ private void internalUpdateGraphicsInterpolated () { BufferStrategy bf = this . getBufferStrategy (); Graphics2D g = null ; try { g = ( Graphics2D ) bf . getDrawGraphics (); render ( g, localTime ); } finally { g. dispose (); } // Shows the contents of the backbuffer on the screen. bf . show(); //Tell the System to do the Drawing now, otherwise it can take a few extra ms u

    //Drawing is done which looks very jerky Toolkit . getDefaultToolkit (). sync (); } Ball [] balls ; BasicStroke ballStroke ; int showMode = 0;

    /** * init Game (override/replace) */ protected void init () { balls = new Ball [ 10];

    int r = 20; for ( int i = 0; i < balls . length ; i ++) { Ball ball = new Ball ( getWidth () / 2, i * 2.5f * r + 80, 10 + i * 300 / ball

    balls [ i ] = ball ; } ballStroke = new BasicStroke ( 3); this . addMouseListener ( new MouseAdapter () {

    @Override public void mouseClicked ( MouseEvent e) { showMode = (( showMode + 1) % 3); } }); } /** * update game. elapsedTime is fixed. (override/replace) * @param elapsedTime

    */ protected void update ( float elapsedTime ) { for ( Ball ball : balls ) { ball . x += ball . vX * elapsedTime ; ball . y += ball . vY * elapsedTime ; if ( ball . x > getWidth () - ball . r ) { ball . vX *= - 1; } if ( ball . x < ball . r ) { ball . vX *= - 1; }

    if ( ball . y > getHeight () - ball . r ) { ball . vY *= - 1; } if ( ball . y < ball . r ) { ball . vY *= - 1; } } }

    /** * render the game (override/replace) * @param g * @param interpolationTime time of the rendering within a fixed timestep (in secon */ protected void render ( Graphics2D g, float interpolationTime ) { g. clearRect ( 0, 0, getWidth (), getHeight ()); if ( showMode == 0) { g. drawString ( "red: raw, black: interpolated (click to switch modes)" , 20, 5 }

  • 8/11/2019 Game Loops! - Java-Gaming

    13/14

  • 8/11/2019 Game Loops! - Java-Gaming

    14/14

    Gingerious So I noticed when I added 15 instead of 10 to the sleep on the variable timestep, it didn't error out on me. Did it error out onanyone else?

    The error was that the time to sleep was a negative value, but it look like it's only for the first time the loop runs. Every time afterthat it's between 8 and 10. Any ideas?

    ManIkWeetI've noticed that the interpolation makes the ball example run smoother, but I fail to understand what the interpolotion valuerepresents, can someone shed some light on this please?

    EDIT: I figured out what it does, but this does not work when trying to smooth a character based on user-input right?

    Loading page 2...

    Pages: [ 1 ] 2 3 IGNORE | PRINT

    You cannot reply to this message, because it is very, very old.

    Check out our latest community projects!

    missing image missing image missing image missing image

    missing image missing image missing image missing image

    Powered by SMF 1.1.18 | SMF 2013, Simple Machines | Managed by Enhanced Four

    http://www.java-gaming.org/topics/iconified/34387/view.htmlhttp://www.java-gaming.org/topics/iconified/34420/view.htmlhttp://www.java-gaming.org/topics/iconified/34426/view.htmlhttp://www.java-gaming.org/topics/iconified/34443/view.htmlhttp://www.java-gaming.org/topics/iconified/34345/view.htmlhttp://www.java-gaming.org/topics/iconified/34346/view.htmlhttp://www.java-gaming.org/topics/iconified/34357/view.htmlhttp://www.java-gaming.org/topics/iconified/34422/view.html