Дрожащий механизм с Graphics2D и двойным буфером

У меня проблемы с маленькой игрой, которую я разрабатываю. хотя я использовал двойной буфер и смог избавиться от мерцания, движение все еще выглядит несколько нервным и не плавным. Я знаю, что это может быть вызвано увеличением движения большими шагами и / или низкой частотой кадров, но у меня все еще остается та же проблема с использованием приращений 1 и 50+ кадров в секунду. Это несколько сложно объяснить, но спрайты странно движутся (правильно, но не в движении жидкости)

Буду признателен, если кто-нибудь укажет мне правильное направление.

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

Я также хотел бы отметить, что X и Y спрайта обрабатываются так, чтобы он не шел вправо, а затем вниз, например, когда он должен был идти по диагонали. Я не думаю, что это проблема в любом случае, так как проблемы возникают даже по прямой линии.

1 ответ

Вы можете попытаться определить, сколько времени потребуется, чтобы нарисовать все, а затем вычесть это из своего времени сна.

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

Это помогает гарантировать, что ваш поток запускается каждые 50 мс (или что бы то ни было sleepTimeBetweenRefresh). Скажем, вы хотите, чтобы он срабатывал каждые 50 мс, ваш рисунок занимает 10 мс Без приведенного выше кода после того, как код выполнялся один раз, вы на самом деле рисовали бы 60 мс после последней краски, потому что вы рисовали 10 и спали 50. Вычитая время, необходимое для рисования компонентов, вы можете сохранить свой FPS стабильным.

Другие вопросы по тегам