0

I´m having some problems with a small game I´m developing. although I´m using double buffer and was able to get rid of flickering, the movement still looks somewhat jittery and not fluid. I know it can be caused by increasing the movement in big steps and/or low framerate, but I still have the same problem using increments of 1 and 50+ fps. It´s somewhat difficult to explain, but the sprites move strangely (correctly, but not in a fluid motion)

I would appreciate if someone could point me in the right direction.

public class Gameplay extends javax.swing.JPanel {

GUI gui;
Canvas canvas = new Canvas();

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
BufferedImage bi;
BufferStrategy buffer;

public Gameplay(GUI gui) {
    this.gui = gui;
    initComponents();
}

// Used to start the gameplay. Called by GUI
public void start() {
    new RefreshScreen().start();
}

// ==============================================================================================
// DOUBLE BUFFER AND PAINTING ===================================================================
// ==============================================================================================
Date date = new Date(); // time
int lastSecond = 0; // seconds controll - for FPS calculation
int fpsCount = 0; // Count frames rendered
int showFps = 0; // The total FPS text that will appear on screen
int MAX_FPS = 30; // targeted Max FPS
int MIN_FPS = 24; // targeted Min FPS
int sleepTimeBetweenRefresh = 20; // Delay before new refresh
Color fpsColor = Color.yellow; // color of the FPS information on screen
String fpsInfo = ""; // Aditional info on FPS (increasing, decreasing, etc)

class RefreshScreen extends Thread {

    public void run() {
        Graphics graphics = null;
        Graphics2D bufferGraphics = null;
        add(canvas, BorderLayout.CENTER);
        canvas.setIgnoreRepaint(true);
        canvas.setVisible(true);
        canvas.setSize(gui.getWidth(), gui.getHeight());
        canvas.createBufferStrategy(2);
        buffer = canvas.getBufferStrategy();
        bi = gc.createCompatibleImage(gui.getWidth(), gui.getHeight());
        bufferGraphics = bi.createGraphics();
        while (true) {
            try {
                //FrameRate count
                date = null;
                date = new Date();
                if (lastSecond != date.getSeconds()) {
                    lastSecond = date.getSeconds();
                    showFps = fpsCount;
                    fpsCount = 0;
                    if (showFps > MAX_FPS) {
                        sleepTimeBetweenRefresh++;
                        fpsInfo = "(--)";
                        fpsColor = Color.blue;
                    }
                    if ((showFps < MIN_FPS) && (sleepTimeBetweenRefresh > 5)) {
                        sleepTimeBetweenRefresh--;
                        fpsInfo = "(++)";
                    }
                    if (showFps < MIN_FPS) {
                        fpsColor = Color.red;
                    }
                    if ((showFps > MIN_FPS) && (showFps <= MAX_FPS)) {
                        fpsColor = Color.green;
                        fpsInfo = "(ok)";
                    }
                }
                fpsCount++;

                //Clear canvas =============================
                bufferGraphics.setColor(Color.black);
                bufferGraphics.clearRect(0, 0, gui.getWidth(), gui.getHeight());

                //FPS =============================
                bufferGraphics.setColor(fpsColor);
                bufferGraphics.drawString("FPS: " + showFps, 3, 15);
                bufferGraphics.setColor(Color.black);

                //SPRITES =============================
                try {
                    for (int count = 0; count < Sprites.getSprites().size(); count++) {
                        bufferGraphics.drawImage(Sprites.getSprite(count).getImage(), Sprites.getSprite(count).getX(), Sprites.getSprite(count).getY(), null);
                    }
                } catch (Exception e) {  }

                //HERO =============================
                try {
                    bufferGraphics.drawImage(Sprites.getHero().getImage(), Sprites.getHero().getX(), Sprites.getHero().getY(), null);
                } catch (Exception e) {  }

                // PAINT BUFFER =================================
                try {
                    graphics = buffer.getDrawGraphics();
                    graphics.drawImage(bi, 0, 0, null);
                    if (!buffer.contentsLost()) {
                        buffer.show();
                    }
                } catch (Exception e) {  }

                // SLEEP  =================================
                sleep(sleepTimeBetweenRefresh);

            } catch (Exception e) {  }
        }//while
    }//run
}//inner class

I would also like to point out that the X and Y of the sprite are being handled so that it doesn´t go right and then down - for instance - when it is supposed to go diagonally. I don´t suppose that´s the problem anyways since the problems occurs even on a straight line.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 1
    1) Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens (and is probably the cause of the 'jittery' rendering). Instead of calling `Thread.sleep(n)` implement a Swing `Timer` for repeating tasks or a `SwingWorker` for long running tasks. See [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) for more details. 2) For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Nov 17 '13 at 19:07
  • 1
    @AndrewThompson I don't think the OP is sleeping on the EDT, he has implemented everything in his own thread. – Harald K Nov 17 '13 at 19:19
  • Why are you using a Canvas on top of a JPanel? Mixing heavy and light weight components never needs well. Swing components are themselves double buffered – MadProgrammer Nov 17 '13 at 19:19
  • @MadProgrammer Thanks for the advice. I´ve removed the JPanel and extended Canvas instead but got the same result. – user3002283 Nov 17 '13 at 20:16
  • @AndrewThompson, Thanks, but I´ve actually created a separated thread for the rendering. It is not freezing the EDT as far as I can see. – user3002283 Nov 17 '13 at 20:17

1 Answers1

0

You might try timing how long it takes to draw everything then subtracting that from your sleep time.

long beforeTime = System.currentTimeMillis();
// do all drawing
sleepTimeBetweenRefresh -= System.currentTimeMillis() - beforeTime;
if(sleepTimeBetweenRefresh < 0) sleepTimeBetweenRefresh = 0;

This helps ensure that your thread is firing every 50ms (or whatever sleepTimeBetweenRefresh is). Say you want it to fire ever 50ms, your drawing takes 10ms. Without the code above after the code ran one time you would actually be painting 60ms after the last paint because you painted for 10 and slept for 50. By subtracting the time it took to paint the components you can keep your FPS stable.

ug_
  • 11,267
  • 2
  • 35
  • 52
  • Thank you, ns47731! That will be the next thing I´ll work on. It is still in early development. But your observation is pitch perfect. – user3002283 Nov 17 '13 at 20:11