[Newbie]Need feedback on achieving smooth animation

Hi all,

First post here. I have been using Java for a few years now but never programmed games. I am learning how to program simple games like pong, etc and will slowly advance to more difficult games. The game loop I understand at a basic level and created pong with a very basic loop, but without using delta time, etc. My loop was literally this:

while(running){
      update(); //move object positions     
      checkCollisions(); //check if objects collided
      repaint();  // call paint to re draw the screen

      Thread.sleep(16) //this is ~60 fps
}

Below is some I code I have written that animates moving square moving back and forth across the screen. So far, I have also figured out how fps is calculated and that is what you sleep by, but that is it really. Unfortunately, the square seems to be jumping during the animation at certain points. The motion is visibly choppy. At the moment, every update I want it to move at a speed/velocity of 5, but it jumps too much and is very noticeable. I also realise it might run slower/faster on your computer. On my machine, fps is getting printed out as 62/63 when the ‘game’ is running. So I have 2 or 3 frames to spare.

    package com.game;

    import java.awt.Dimension;
    import java.awt.Graphics;
    import static java.util.concurrent.TimeUnit.NANOSECONDS;
    import javax.swing.JFrame;

    import javax.swing.JPanel;

    class GamePanel extends JPanel implements Runnable {

        //Panel specific stuff
        private static Dimension dimension = new Dimension(800, 400);

        //Square drawing stuff
        private double x = 0, y = 0;
        private double xDir = 5;
        private double squareWidth = 50;

        //Game thread stuff
        private Thread gameThread = new Thread(this);
        private boolean running = true;
        private long targetFps = 1000 / 60;

        public GamePanel() {
            setPreferredSize(dimension);
        }

        @Override
        public void run() {
            gameLoop();
            System.out.println("Game loop complete");
            //System.exit(0);
        }

        private void gameLoop() {
            int fps = 0;
            long timeAtLoopStart = 0;
            long timeAtLoopEnd = 0;
            long frameTime = 0;
            long loopTimer = NANOSECONDS.toSeconds(System.nanoTime());
            while (running) {
                timeAtLoopStart = NANOSECONDS.toSeconds(System.nanoTime());
                fps++;

                if (timeAtLoopStart == loopTimer + 1) {
                    System.out.println("FPS is: " + fps);
                    fps = 0;
                    loopTimer = NANOSECONDS.toSeconds(System.nanoTime());
                }

                //check fps to see if we are over our target fps
                if(loopFPS > FPS){
                //if we are here we have processed more frames than our target fps in one second
                    // do something??
                }

                update();
                checkCollisions();
                repaint(); //paintComponents

                timeAtLoopEnd = NANOSECONDS.toSeconds(System.nanoTime());
                frameTime = timeAtLoopEnd - timeAtLoopStart;
                // System.out.println(frameTime);
                try {
                    Thread.sleep(targetFps);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }

        private void update() {
            x = x + xDir; //use delta time here somehow?
            if (x + squareWidth == dimension.width) {
                xDir = -5; //go left if we hit the right side
            }
            if (x == 0) {
                xDir = 5; //go right if we hit the left side
            }
        }

        private void checkCollisions() {
               //TODO
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.fillRect((int) x, (int) y, (int)squareWidth, (int)squareWidth);
        }

        public void start() {
            gameThread.start();
        }

        public static void main(String[] args) {
            JFrame jf = new JFrame("Game");
            GamePanel gp = new GamePanel();
            jf.add(gp);
            jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jf.setLocationByPlatform(true);
            jf.pack();
            jf.setVisible(true);
            gp.start();
        }
    }

Sorry for the long post, but I would like to (using this code) to achieve smooth motion using whatever speed I give xDir. I have seen articles related to game loops, etc. and have tried to use them but I am not understanding them correctly. Maybe I am and the code above is actually fine, and I am just being to critical of the animation. Could someone please run the above code and give feedback?

Just to note: I tried avoiding the repaint method in a further update of the above code and used the “double buffer” strategy where you draw to a separate image and then paint the whole image to the jpanel. That didn’t smooth out the motion either so I reverted back to repaint (for no particular reason I might add).

Thanks

From the next time, please surround your code in [nobbc]

[/nobbc] tags. That will help the viewers read the code because then the code will be highlighted. Coming to your question, the answer is to stop using the [icode]Thread.sleep(long)[/icode] method in the game loop. This is because it is not guaranteed that it will sleep exactly for the requested amount of time.

Before going on further on game loops, it is better if you use Active Rendering, by using BufferStrategy, which you can create from a JFrame. This is some sample code for creating a JFrame with a BufferStrategy.


// Create and show the JFrame
JFrame frame = new JFrame(“My Game Window”);
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);

// Create the BufferStrategy now with two back buffers
frame.createBufferStrategy(2);
BufferStrategy strategy = frame.getBufferStrategy();

// Now start the game loop
while (running)
{
// Update the game and check for collisions
gameUpdate();
checkCollisions();

// Now draw a frame
do
{
    do
    {
        Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
        renderGame(g2d);
        g2d.dispose();
    }
    while (strategy.contentsRestored());
}
while(strategy.contentsLost());

}


The above loop is very horrible, it has no timing mechanism. There are three types of game loops, fixed-timestep, variable-timestep and hybrid ones. I prefer the first kind, fixed-timestep. Here are some articles that I prefer (read them in the same order).


[*] http://www.koonsolo.com/news/dewitters-gameloop/
[*] http://gameprogrammingpatterns.com/game-loop.html
[*] http://gafferongames.com/game-physics/fix-your-timestep/
[*] http://docs.unity3d.com/Manual/ExecutionOrder.html


The last link shows how Unity3D does it's game loop, it's very complex, so read that only once you are comfortable with your game loop.

Hope this helps.

Hi, thanks for the info. I didn’t have the time to try your kilobits will look at it and the links tomorrow. Cheers

Your program ran quite smoothly for me. (I commented out the FPS measurement coding at the top of the loop.)

Using a fixed sleep amount is not terrible, as long as there is a minimum of variability in the rest of the program. Often people take a time stamp at the top of the game loop, and measure the elapsed time for the update and render, then subtract that from the target sleep amount, and sleep the result. For many simple applications this is quite adequate.

Another strategy is to use a util.Timer (not Swing.Timer), or a ScheduledThreadPoolExecutor with a time increment set via ScheduleAtFixedRate. With this, there is no need to worry about measuring elapsed time.

Hi again,

I never got back to you about your last post! The buffered strategy from JFrame or Canvas… I’ve read that these are redundant as JPanel has double buffering built in by default. Could you elaborate on this and justify the selection of one over the other (JPanel and Canvas).

Thanks

Yes, JPanel has built-in double buffering, but it doesn’t support active rendering. You have to wait for the EDT to repaint the GUI for you.