Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

1361981 Posts in 63512 Topics- by 55392 Members - Latest Member: SeanWallaceArt

June 25, 2019, 02:42:01 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Dealing with Stuttering - Java, Game Maker, and DirectX considered
Pages: 1 2 [3] 4 5 6
Print
Author Topic: Dealing with Stuttering - Java, Game Maker, and DirectX considered  (Read 34954 times)
Chromanoid
Level 10
*****



View Profile
« Reply #40 on: October 09, 2011, 02:48:36 AM »

Can you post the complete code of a simple sample for investigation (something I can run with an IDE)? Casting System.nanoTime() to double can lead to a loss of precision. You may want to use long. Since double has a 52 bits mantissa this should not become a problem soon Smiley. @all who think it is related to java performance: one can get smooth moving images with javascript and canvas, do you really think java2d is less powerful?
« Last Edit: October 09, 2011, 03:22:25 AM by Chromanoid » Logged
_Tommo_
Level 8
***


frn frn frn


View Profile WWW
« Reply #41 on: October 09, 2011, 03:14:55 AM »

-Java stutters because of its memory management system, you can't avoid that a frame will take lots more time because the thread was stopped by the GC...
you can only create less short-lived objects using pooling and avoiding using the standard library

-Java stutters because of OpenGL - I found that on Windows OpenGL is just bad in terms of fluidity on some drivers, be it used from C++, Java, whatever. In particular if you use immediate mode (glBegin-glEnd) the thing is crap.

GM is much more stabler instead because it's written in native languages and it's Dx on Windows, so they can be confident on how much time a frame takes.

tl;dr: Java stutters on Windows and always will.
Logged

Chromanoid
Level 10
*****



View Profile
« Reply #42 on: October 09, 2011, 03:18:41 AM »

The GC is not the problem here. You can watch GC activity with JConsole. Look at the sample before you post. Please stop this Java dissing. There are so many games that use Java2D without any stuttering. Just look at http://www.java4k.com.
Logged
_Tommo_
Level 8
***


frn frn frn


View Profile WWW
« Reply #43 on: October 09, 2011, 03:20:56 AM »

The GC is not the problem here. You can watch GC activity with JConsole. Look at the sample before you post. Please stop this Java dissing. There are so many games that use Java2D without any stuttering just look at http://www.java4k.com.

I'm not saying that java HAS to stutter, it won't if you know how to manage memory, but it will, and will do pretty badly, if you start allocating hashmaps all over the place thinking allocation is free.
I saw it done by Java beginners, they have the insistent idea that a new each frame is something nice.
Logged

Chromanoid
Level 10
*****



View Profile
« Reply #44 on: October 09, 2011, 03:25:36 AM »

As I said before the stuttering here occurs not because of GC activity. Beside this XNA, Flash etc. have a GC too. Also Java's GC+JIT Compiler are very good, using object pools can affect the optimization and performance in a negative way - see http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html. A new each frame is not genrally a problem.

I checked the fixed gameloop from the post on JGO. The loop posted in the OP has the same problems. You should not borrow code from the OP Smiley.
« Last Edit: October 09, 2011, 04:34:36 AM by Chromanoid » Logged
Chromanoid
Level 10
*****



View Profile
« Reply #45 on: October 09, 2011, 07:21:41 AM »

I think your problem is that you are not interpolating the movement of the sprites (between the timesteps). I wrote a small program inspired by the JBullet fixed timestep loop and the render function from gp wiki to illustrate the issue:
Code: (java)
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);
    }

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

            @Override
            public void run() {
                long start = System.nanoTime();
                while (true) {
                    long now = System.nanoTime();
                    float elapsed = (now - start) / 1000000000f;
                    start = now;
                    updateWithFixedTimeStep(elapsed, maxSubSteps, fixedTimeStep);
                    if (1000000000 * fixedTimeStep - (System.nanoTime() - start) > 1000000) {
                        try {
                            Thread.sleep(0, 999999);
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
        }.start();
    }

    private void updateWithFixedTimeStep(float elapsedSeconds, int maxSubSteps, float fixedTimeStep) {
        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 down to a halt
            int clampedSubSteps = (numSubSteps > maxSubSteps) ? maxSubSteps : numSubSteps;
            for (int i = 0; i < clampedSubSteps; i++) {
                update(fixedTimeStep);
            }
        }

        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 until
        //Drawing is done which looks very jerky
        Toolkit.getDefaultToolkit().sync();
    }
    Ball[] balls;
    BasicStroke ballStroke;
    int showMode = 0;

    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 / balls.length, 0, r);
            balls[i] = ball;
        }
        ballStroke = new BasicStroke(3);
        this.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                showMode = ((showMode + 1) % 3);
            }
        });
    }

    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;
            }
        }
    }

    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, 50);
        }
        if (showMode == 1) {
            g.drawString("red: raw (click to switch modes)", 20, 50);
        }
        if (showMode == 2) {
            g.drawString("black: interpolated (click to switch modes)", 20, 50);
        }
        for (Ball ball : balls) {
            g.setStroke(ballStroke);
            if (showMode == 0 || showMode == 1) {
                //w/o interpolation
                g.setColor(Color.RED);
                g.drawOval((int) (ball.x - ball.r), (int) (ball.y - ball.r), (int) ball.r * 2, (int) ball.r * 2);
            }
            if (showMode == 0 || showMode == 2) {
                //with interpolation
                g.setColor(Color.BLACK);
                g.drawOval((int) (ball.x - ball.r + interpolationTime * ball.vX), (int) (ball.y - ball.r + interpolationTime * ball.vY), (int) ball.r * 2, (int) ball.r * 2);
            }
        }
    }

    public static class Ball {

        public float x, y, vX, vY, r;

        public Ball(float x, float y, float vX, float vY, float r) {
            this.x = x;
            this.y = y;
            this.vX = vX;
            this.vY = vY;
            this.r = r;
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Create and display the form */
        new Thread() {

            {
                setDaemon(true);
                start();
            }

            public void run() {
                while (true) {
                    try {
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (Throwable t) {
                    }
                }
            }
        };
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                Game game = new Game();
                game.setVisible(true);
                game.start(1 / 60f, 5);
            }
        });
    }
}

btw this seems interesting: http://entropyinteractive.com/2011/02/game-engine-design-the-game-loop/
« Last Edit: October 10, 2011, 05:17:12 AM by Chromanoid » Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #46 on: October 09, 2011, 07:38:04 AM »

The GC is not the problem here. You can watch GC activity with JConsole. Look at the sample before you post. Please stop this Java dissing. There are so many games that use Java2D without any stuttering just look at http://www.java4k.com.

I'm not saying that java HAS to stutter, it won't if you know how to manage memory, but it will, and will do pretty badly, if you start allocating hashmaps all over the place thinking allocation is free.
I saw it done by Java beginners, they have the insistent idea that a new each frame is something nice.

The problem isn't as much with the language itself but rather with the attitude that often comes with the language. Courses, books and even professionals spread the notion that allocation in Java is free-of-interest fire-and-forget. Of course this will be a problem in resource-constrained contexts, which all games constitutes. And of course Java can be used to make games IF the same type of thinking that goes into game development using other languages are applied when using Java, which unfortunately it often isn't.

So therefore, GC is a common though by no means a necessary source of stuttering in Java applications and games.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
lasttea999
Level 2
**


View Profile
« Reply #47 on: October 09, 2011, 04:22:50 PM »

Thanks for all the feedback!

Whoops sorry I guess that's why I had a zip file in the folder too.  I guess you probably want the zip and everything it has in it...

https://sites.google.com/site/nitsudrhodes/SavageBalloon3001.zip?attredirects=0&d=1

It works now, thank you so much! It doesn't look like there's any stuttering, but I think it may be a bit difficult to tell because everything's moving so fast. Any chance you could make an example like simple_test?

Best way to test it is just to run your code in variable step (simply add no timing mechanisms and just update). You should have a fluid picture (variable step is the king of smoothness). If not then one of the things I said have to be true, you are using the wrong framework/setup.

Well, it still seems to stutter... I still kinda hope that this is the case, though:

@all who think it is related to java performance: one can get smooth moving images with javascript and canvas, do you really think java2d is less powerful?

---

Quote
Can you post the complete code of a simple sample for investigation (something I can run with an IDE)?

Well, here is the source (box.net download). Note: it's not very well commented, and many of the comments are fragments from various tutorials. The two sample programs correspond to SimpleTest.java and TestGame.java; however, these have been updated a bit since I posted the JARs (for example, simple_test can now attempt to interpolate).

-Java stutters because of its memory management system, you can't avoid that a frame will take lots more time because the thread was stopped by the GC...
you can only create less short-lived objects using pooling and avoiding using the standard library
Quote
I'm not saying that java HAS to stutter, it won't if you know how to manage memory

I'm afraid this is beyond my expertise; any suggestions?

Quote
-Java stutters because of OpenGL - I found that on Windows OpenGL is just bad in terms of fluidity on some drivers, be it used from C++, Java, whatever. In particular if you use immediate mode (glBegin-glEnd) the thing is crap.

GM is much more stabler instead because it's written in native languages and it's Dx on Windows, so they can be confident on how much time a frame takes.

tl;dr: Java stutters on Windows and always will.

I'm starting to think that this is the case; remember, I tried using LWJGL and it stuttered too, although, as I think dustin suggested, in the case of LWJGL I may have just implemented it incorrectly.

I have mixed feelings about this conclusion; if it's true, then on the one hand, my programs are always going to stutter and there's nothing more I can do, so I can just grit my teeth and start making programs; on the other hand, the stuttering will never go away.

Quote
I think your problem is that you are not interpolating the movement of the sprites (between the timesteps).

I do interpolate, but it's... abstracted? so it's not written directly in the main game loop. At least, that's the case with sprites (or rather, with instances of the GameUnit class). The interpolation occurs between renders, though.

Quote
I wrote a small program inspired by the JBullet fixed timestep loop and the render function from gp wiki to illustrate the issue:

Still stuttered a little bit. Maybe it is just my computer... I realized that the monitor (for my desktop) is a little bit older than the computer, so maybe they're not "working together" very nicely?

Interestingly, I discovered that the monitor's refresh rate could be changed from 60 Hz to 75 Hz. All of the really visible, "big" stuttering seemed to go away, but in its place I could just barely detect what I'll describe as "miniscule" stuttering... more like a sort of fuzzy glow-type thing. However, under these circumstances the monitor started giving out a weird hum, so I switched back to 60 Hz...

The problem isn't as much with the language itself but rather with the attitude that often comes with the language. Courses, books and even professionals spread the notion that allocation in Java is free-of-interest fire-and-forget. Of course this will be a problem in resource-constrained contexts, which all games constitutes. And of course Java can be used to make games IF the same type of thinking that goes into game development using other languages are applied when using Java, which unfortunately it often isn't.

So therefore, GC is a common though by no means a necessary source of stuttering in Java applications and games.

As with _Tommo_, allow me to ask what you would suggest to address this issue.

EDIT: Also, what do you guys think of these two points:

-Do game programs seem to stutter less (does the stuttering become less noticeable) the "busier" things get?

-Do Applets run faster than JARs?

ANOTHER EDIT: I tested my programs on a Mac (laptop) for the first time... I found the second build didn't run at all. For game_test, the first build seemed to run on the Mac than on our other computers most of the time, but it would periodically go down to 44 FPS...
« Last Edit: October 09, 2011, 08:24:15 PM by lasttea999 » Logged

Bryant
Level 0
***



View Profile WWW
« Reply #48 on: October 09, 2011, 06:43:08 PM »

Here is an example for one single sprite to make the principle clear. You can apply it to all translating objects the same way. I chose 50 fps on purpose since it would jump like hell without interpolation. But interpolation will do wonders. Vsync is on.

(control with w,s,a,d, esc to exit, you need to install it)
http://depositfiles.com/files/irfny4vct

And here is the main XNA code. (Even when I turn off interpolation this fixed timestep implementation will stutter less than the native xna-solution in general)
Let me know how it runs.

  protected override void Update(GameTime gameTime)
        {
           elapsedTime += gameTime.ElapsedGameTime.TotalSeconds;

           if (elapsedTime > 3 * UpdateIntervall)  //In extreme slowdowns don't update more
            elapsedTime = 3.01 * UpdateIntervall;  //than 3 times in a row, rather slow
                                                   //down for better input-response
 
...

Hey J-Snake, what do you set your UpdateInterval to? 1/60? And this code doesn't stutter for you? I can't test it on my end as I only have access to Macs Sad

EDIT: Oops, I must have skipped over your first line. So you set UpdateInterval to 1/50?  And you have both vsync and XNA's FixedTimeStep property set to false?
Logged

lasttea999
Level 2
**


View Profile
« Reply #49 on: October 09, 2011, 07:10:24 PM »

So, uh... I tried the following suggestion from J-Snake another time:

Best way to test it is just to run your code in variable step (simply add no timing mechanisms and just update). You should have a fluid picture (variable step is the king of smoothness). If not then one of the things I said have to be true, you are using the wrong framework/setup.

specifically, by commenting out the following code:

Code:
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
Thread.yield();

//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
try {Thread.sleep(1);} catch(Exception e) {}

now = System.nanoTime();
}

and, while I've only tested it on my desktop with software I don't usually use anymore, the stuttering seems to have improved significantly. I think it might almost be on the level of Game Maker.

However, this way the program doesn't use Thread.yield or Thread.sleep; isn't that difficult and/or stressful for the computer, or something? I guess I'll have to test on some other computers?

EDIT: I don't think that method is very nice for my laptop...

Also, @Chromanoid: how exactly does this work?

Code:
//Tell the System to do the Drawing now, otherwise it can take a few extra ms until
//Drawing is done which looks very jerky
Toolkit.getDefaultToolkit().sync();
« Last Edit: October 09, 2011, 08:25:29 PM by lasttea999 » Logged

J-Snake
Level 10
*****


A fool with a tool is still a fool.


View Profile WWW
« Reply #50 on: October 10, 2011, 02:30:39 AM »

EDIT: Oops, I must have skipped over your first line. So you set UpdateInterval to 1/50?  And you have both vsync and XNA's FixedTimeStep property set to false?
XNA's FixedTimeStep is set to false since I am implementing my own time-stepping inside the variable step. Vsync is on. UpdateIntervall is 1/50.  The first program is interpolated, the second is not. You will be satisfied with the interpolation once you get the chance to run it, just try it on someone else's pc.

@lattea: I am suggesting to run something in the regular variable loop (better turn vsync on for it) simply to test that your framework or setup is supporting it. If it runs smoothly it is likely the case but if it stutters frequently or runs significantly unsatisfying then it is surely not the case and no interpolation will ever help. For example Flash will always stutter.

Just make clear how game-loops work.

while(now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
      update();
      lastUpdateTime += TIME_BETWEEN_UPDATES;
      updateCount++;
   }

Make sure that this loop is always executed just once to resolve the fixed timestep, it means comment out the while-line.


« Last Edit: October 10, 2011, 02:57:14 AM by J-Snake » Logged

Independent game developer with an elaborate focus on interesting gameplay, rewarding depth of play and technical quality.<br /><br />Trap Them: http://store.steampowered.com/app/375930
_Tommo_
Level 8
***


frn frn frn


View Profile WWW
« Reply #51 on: October 10, 2011, 02:47:19 AM »

As with _Tommo_, allow me to ask what you would suggest to address this issue.

Well, you should at least conceptually divide the code that is "load time" and the code that is "runtime".
In load time code you create anything you can be sure will be eventually needed, and in runtime you only allocate memory if it's strictly necessary. This is mantra for C++ but it's even more important for Java as it allows the GC to slack (generationals GC can detect long-lived objects and avoid to check them each time).

Also, it depends much on how you're using OpenGL, as the golden rule on windows is: "bus makes you stutter".
So, you should only upload things in VRAM at load time, using VBOs, textures etc, and only access them at runtime.
This is always true everywhere, but in my experience Windows drivers are A LOT worse at managing streaming.

So, glDrawArrays with local arrays is bad, glBegin() - glEnd() immediate mode is a capital sin, and you should definitely use VBOs.

Also, locking the framerate at some sane amount that you can always meet (60-30) keeps the logic stabler (yeah, there's variable timestep, but it has some hardly manageable drawbacks) and avoids most "psycological" hiccups (those that you feel when the FPS abruptly changes).
Logged

Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #52 on: October 10, 2011, 02:55:17 AM »

Chiming in with _Tommo_, try to pre-allocate all the resources you need before starting the time-critical part, and make certain they're not deallocated during its execution. In a real-time GC context it is better to allocate placeholder resources that you later initialise to what you need or mark as inactive and available for reinitialisation when you're done with them than to allocate them on the fly and let the GC deallocate them at its whim.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
Chromanoid
Level 10
*****



View Profile
« Reply #53 on: October 10, 2011, 04:47:50 AM »

So, uh... I tried the following suggestion from J-Snake another time:

Best way to test it is just to run your code in variable step (simply add no timing mechanisms and just update). You should have a fluid picture (variable step is the king of smoothness). If not then one of the things I said have to be true, you are using the wrong framework/setup.

specifically, by commenting out the following code:

Code:
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
Thread.yield();

//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
try {Thread.sleep(1);} catch(Exception e) {}

now = System.nanoTime();
}

and, while I've only tested it on my desktop with software I don't usually use anymore, the stuttering seems to have improved significantly. I think it might almost be on the level of Game Maker.

However, this way the program doesn't use Thread.yield or Thread.sleep; isn't that difficult and/or stressful for the computer, or something? I guess I'll have to test on some other computers?

EDIT: I don't think that method is very nice for my laptop...

Also, @Chromanoid: how exactly does this work?

Code:
//Tell the System to do the Drawing now, otherwise it can take a few extra ms until
//Drawing is done which looks very jerky
Toolkit.getDefaultToolkit().sync();
You don't need Thread.yield (it even maxes your processing time), sleep should do the same http://www.jguru.com/faq/view.jsp?EID=425624.

I got this getDefaultToolkit().sync stuff from http://content.gpwiki.org/index.php/Java%3ATutorials%3ADouble_Buffering
The javadoc states that it forces any stuff that is buffered  by the window system to draw on screen.

You should really post this at java-gaming.org, there are more active members who make commercial games in Java.
I am convinved that your game stutters because of the gameloop. This is why switching to lwjgl or directx would not change anything.
Did you try some game from java4k.com? Do they stutter? If it is a Java problem they should.

Does my code really stutter on your system? Because it's smooth here.

PS I revisited my code (basically just added a sleeper thread). I don't see any stuttering. It renders as often it can and sleeps 1ms if there is time for it.
« Last Edit: October 10, 2011, 05:25:55 AM by Chromanoid » Logged
lasttea999
Level 2
**


View Profile
« Reply #54 on: October 10, 2011, 06:36:53 PM »

Just make clear how game-loops work.

while(now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
      update();
      lastUpdateTime += TIME_BETWEEN_UPDATES;
      updateCount++;
   }

Make sure that this loop is always executed just once to resolve the fixed timestep, it means comment out the while-line.

Yup, it still seems to stutter. The more I do this stuff, though, the more unpredictable my sensitivity to stuttering seems to get...



Ah, thank you so much. Let me first say that I apologize for my lack of expertise; I currently tend to have trouble understanding anything significantly more complicated than GML... That being said, is the main message here to load images and whatnot when the game starts, rather than as they're needed?

Did you try some game from java4k.com? Do they stutter? If it is a Java problem they should.

I did, actually, and if I remember correctly, some did stutter.

Quote
Does my code really stutter on your system? Because it's smooth here.

Upon further testing, I found that, on my laptop, it doesn't really stutter even without the sleeper thread. On my desktop, it needs the sleeper thread to run without stuttering, but in that case the circles without interpolation don't stutter either.

I think you may have succeeded!  Tears of Joy May I use your code to make another RenderUnit class? (EDIT: That is, may I implement your code in my program?)

EDIT: Is it necessary to start another thread in programs like this, as in your start(fixedTimeStep, maxSubSteps) method? It seems that some examples do this while others don't.
« Last Edit: October 10, 2011, 10:00:08 PM by lasttea999 » Logged

Chromanoid
Level 10
*****



View Profile
« Reply #55 on: October 11, 2011, 02:21:19 AM »

Of course you can use it! I myself just read through the fixed timestep code of jbullet and the code from gpwiki for rendering.
EDIT: Is it necessary to start another thread in programs like this, as in your start(fixedTimeStep, maxSubSteps) method? It seems that some examples do this while others don't.
mmh I don't know. Just try without it Smiley. I am unexperienced in Java game creation. I currently just use JMonkeyEngine3 where I don't have to bother about those things. I thought: Since the Frame is started by the EventQueue it would block the queue if a loop is started from there. At least marking the thread as Daemon might be a good idea.

Maybe you can try to sleep just one ms in your Unit and drop the yield. If it stutters maybe you should change the drawing code to Java provided doublebuffering. Together with a Thread that sleeps forever this could be enough.
« Last Edit: October 11, 2011, 02:43:58 AM by Chromanoid » Logged
lasttea999
Level 2
**


View Profile
« Reply #56 on: October 11, 2011, 07:51:47 AM »

Of course you can use it!

Thank you, Chromanoid, and everyone else that contributed to this thread! I'll try to see what happens when I fit the code into the framework (?) that I already have and report results later. I don't know when that will be, though, because I tend to be fairly busy, and may not be able to work on this just yet.

Of course, since your code works as-is, if it doesn't work in my framework, then it will probably mean that I broke something in the process of changing things to fit (although I'll try to leave everything alone as much as possible), so all I have to do is go back to the original code and try to make it fit again.

Quote
I myself just read through the fixed timestep code of jbullet and the code from gpwiki for rendering.

Should I credit someone related to those parties when I use the code, then?

Quote
Maybe you can try to sleep just one ms in your Unit and drop the yield. If it stutters maybe you should change the drawing code to Java provided doublebuffering.

I think I've tried this already without much improvement (and the build that uses yield already uses double buffering), and

Quote
Together with a Thread that sleeps forever this could be enough.

this method seems to work well on my desktop, but not my laptop. So I guess I still don't quite understand what the crucial difference is between your code and mine, but I'll investigate!  Smiley
« Last Edit: October 11, 2011, 12:27:13 PM by lasttea999 » Logged

Chromanoid
Level 10
*****



View Profile
« Reply #57 on: October 11, 2011, 03:43:24 PM »

Should I credit someone related to those parties when I use the code, then?
Mmh I don't know -.-. This is what scares me about Open Source Licenses...
http://content.gpwiki.org/index.php/Java%3ATutorials%3ADouble_Buffering
http://code.google.com/p/jbullet-jme/source/browse/branches/jbullet/src/com/bulletphysics/dynamics/DiscreteDynamicsWorld.java?spec=svn555&r=555#294
Are my sources (a mix of inspiration and copy & paste).

Btw I looked up the sleep code in lwjgl. lwjgl seems to use the native high precision timer of the respective os. lwjgl's "sync" function can be found at http://massapi.com/source/lwjgl-source-2.7.1/src/java/org/lwjgl/opengl/Display.java.html line 419. I am afraid it has something to do with this.
Logged
lasttea999
Level 2
**


View Profile
« Reply #58 on: October 11, 2011, 03:50:19 PM »


Hmmmmmm... I guess I'll start off by including those links, then, as well as a link to this article...

Quote
Btw I looked up the sleep code in lwjgl. lwjgl seems to use the native high precision timer of the respective os. lwjgl's "sync" function can be found at http://massapi.com/source/lwjgl-source-2.7.1/src/java/org/lwjgl/opengl/Display.java.html line 419. I am afraid it has something to do with this.

What is this stuff  Shocked

EDIT: ...I think I broke it. I modified your code, and it's not running as well as your code on the same computer. Currently trying to test various features to see what the issue is...

EDIT EDIT: However, I seem to get similar results to your code if I use just your timing system in the first build I already had...? So what changed between that build and the one I just made...?
« Last Edit: October 11, 2011, 06:08:06 PM by lasttea999 » Logged

Chromanoid
Level 10
*****



View Profile
« Reply #59 on: October 11, 2011, 06:26:44 PM »

Another interesting topic about this: http://www.java-gaming.org/index.php?topic=22762.0
They say it's about VSync... There are some solutions with native code: http://today.java.net/pub/a/today/2006/02/23/smooth-moves-solutions.html

I revisited my code to sleep more Smiley:
Code:
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 */
    double localTime = 0f;

    /** Creates new form Game */
    public Game() {
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setIgnoreRepaint(true);
        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 time.
     */
    public final void start(final double fixedTimeStep, final int maxSubSteps) {

            this.createBufferStrategy(2);
        init();
        long start = System.nanoTime();
        long step=(long)Math.floor(1000000000d * fixedTimeStep);        
        while (true) {
            long now = System.nanoTime();
            double elapsed = (now - start) / 1000000000d;
            start = now;
            internalUpdateWithFixedTimeStep(elapsed, maxSubSteps, fixedTimeStep);
            internalUpdateGraphicsInterpolated();
            while (true) {        
                Thread.yield();
                long delta = start + step - System.nanoTime();
                if (delta <= 0) {
                    break;
                }                
                try {
                    Thread.sleep(1);
                } catch (InterruptedException ex) {
                }
            }
        }
    }

    /**
     * Updates game state if possible and sets localTime for interpolation.
     * @param elapsedSeconds
     * @param maxSubSteps
     * @param fixedTimeStep
     * @return count of processed fixed timesteps
     */
    private int internalUpdateWithFixedTimeStep(double elapsedSeconds, int maxSubSteps, double fixedTimeStep) {
        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 down to a halt
            int clampedSubSteps = (numSubSteps > maxSubSteps) ? maxSubSteps : numSubSteps;
            for (int i = 0; i < clampedSubSteps; i++) {
                update(fixedTimeStep);
            }
            return clampedSubSteps;
        }
        return 0;
    }

    /**
     * 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 until
        //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[20];
        int r = 20;
        for (int i = 0; i < balls.length; i++) {
            Ball ball = new Ball(getWidth() / 2, (getHeight() - 120) / (balls.length + 1f) * (i + 1f) + 60f, 10f + i * 300f / balls.length, 0, r);
            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.
     * @param elapsedTime
     */
    protected void update(double 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
     * @param g
     * @param interpolationTime time of the rendering within a fixed timestep (in seconds)
     */
    protected void render(Graphics2D g, double interpolationTime) {
        g.clearRect(0, 0, getWidth(), getHeight());
        if (showMode == 0) {
            g.drawString("red: raw, black: interpolated (click to switch modes)", 20, 50);
        }
        if (showMode == 1) {
            g.drawString("red: raw (click to switch modes)", 20, 50);
        }
        if (showMode == 2) {
            g.drawString("black: interpolated (click to switch modes)", 20, 50);
        }
        for (Ball ball : balls) {
            g.setStroke(ballStroke);
            if (showMode == 0 || showMode == 1) {
                //w/o interpolation
                g.setColor(Color.RED);
                g.drawOval((int) (ball.x - ball.r), (int) (ball.y - ball.r), (int) ball.r * 2, (int) ball.r * 2);
            }
            if (showMode == 0 || showMode == 2) {
                //with interpolation
                g.setColor(Color.BLACK);
                g.drawOval((int) (ball.x - ball.r + interpolationTime * ball.vX), (int) (ball.y - ball.r + interpolationTime * ball.vY), (int) ball.r * 2, (int) ball.r * 2);
            }
        }
    }

    public static class Ball {

        public double x, y, vX, vY, r;

        public Ball(double x, double y, double vX, double vY, double r) {
            this.x = x;
            this.y = y;
            this.vX = vX;
            this.vY = vY;
            this.r = r;
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Create and display the form */
        new Thread() {

            {
                setDaemon(true);
                start();
            }

            public void run() {
                while (true) {
                    try {
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (Throwable t) {
                    }
                }
            }
        };
        Game game = new Game();
        game.setVisible(true);
        game.start(1 / 120d, 5);
    }
}

Now I run with 120 updates per second and switched some numbers to double. I think there are some problems/bugs refreshing the painted component. Bombarding Java with screen updates seems to help with this issue. Maybe the best thing would be to decouple drawing and updating. To stay low at processor usage the drawing rate should be capped, but at a much higher level than the game update. Currently I just increased both rates. In my prevoius code I "capped" the drawing rate only at ~1000hz because of the sleep(1).
When you cannot repaint at a high rate the stuttering will *probably* begin to appear. If you really want to use Java2D you should take a look at the above mentioned VSync stuff.

Maybe it is time to switch to lwjgl. Remember to enable vsync when doing so Smiley. http://lwjgl.org/wiki/index.php?title=Main_Page#Getting_started
I really was not aware that Java2D has so many pitfalls Sad.
« Last Edit: October 11, 2011, 07:15:45 PM by Chromanoid » Logged
Pages: 1 2 [3] 4 5 6
Print
Jump to:  

Theme orange-lt created by panic