Символ JavaGame не движется точно во время рисования?

Для личной практики я переделываю Flappy bird для настольных компьютеров на Java, мне удалось добиться генерации всех опор, перемещения экрана, bg, но теперь у меня есть одна проблема - производительность.

Иногда я чувствую, что игра движется не так быстро, а иногда застревает на 0,5 секунды или еще что-то, но это не тот случай, когда я двигаю свою птицу, она движется немного странно, похоже, что она движется слишком сильно вперед, а затем назад смотрите gif в формате MP4:

http://gyazo.com/d7e94c0b772192e5a5dd1d2b61b8c529

Что может быть причиной этого? это может быть мой игровой цикл или то, как я рисую графику? Я не использую двойную буферизацию, я выполняю рендеринг, просто вызывая.repaint в jframe, который перерисовывает добавленную мной JPanel.

Игровой цикл:

private void gameLoop() {
    new Thread() {
        private long last;
        private long start;
        private int wait;
        public void run() {
            while(game) {
                long now = System.nanoTime();
                long length = now - lastLoop;
                lastLoop = now;
                double delta = length / ((double) Constants.OPTIMAL);
                lastFps += length;
                fps++;
                if (lastFps >= 1000000000) {
                    System.out.println("FPS: " + fps);
                    lastFps = 0;
                    fps = 0;
                }
                update(delta);
                render();
                this.sleep
            }
        }
        private void sleep() {
            try {
                Thread.sleep((this.last - System.nanoTime() + Constants.OPTIMAL) / 1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }.start();
}

Моя область рисования:

public void paintComponent(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    int translateAmount = this.game.getLevel().getTranslate();
    g.translate(-translateAmount, 0);
    this.background.render(g2d);        
    this.game.getLevel().renderLevel(g2d);
    g.translate(0, 0);
    this.game.getBird().render(g2d);

}

Вот как я рисую фон: я добавляю фон один за другим, и если фон находится за рамкой, я не буду его рендерить.

public void render(Graphics2D g) {
    Iterator<RepeatedBackground> itr = this.repeats.iterator();
    while (itr.hasNext()) {
        RepeatedBackground b = itr.next();
        if (this.game.getLevel().getTranslate() - b.getX() >= 300) {
            itr.remove();
            continue;
        }
        if (b.getX() - this.game.getLevel().getTranslate() < Constants.WIDTH) {
            b.render(g);
        }
    }
}

Вот как я двигаю свою птицу (я не использую дельту, я использовал некоторые уроки по этому вопросу):

private void update(double delta) {
    if (System.currentTimeMillis() - this.level.getTime() >= Constants.TRANSLATE_SPEED) {

        // move background
        this.level.updateTranslate();
        this.level.setTime();

        // move bird
        this.getBird().move();
    }
}

public void move() {
    this.x += 2 / 1.10;
}

Что может быть причиной отставания птицы или фона? Что-то не так с моими способами рендеринга или игровым циклом?

Fps всегда печатает это:

FPS: 1724172
FPS: 1551857
FPS: 1494378
FPS: 1471987
FPS: 1434095
FPS: 1629905

1 ответ

Решение

Ваш игровой цикл смущает меня - но это не сложно;)

В принципе, как я понимаю эти вещи, это должно работать что-то вроде...

while (gamming) {
    now = get current time;
    update game state
    delta = get current time - now
    delay = desired delay - delta
    wait for delay
}

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

Теперь я не уверен, что ваш delta Значения должны быть... время через текущую секунду?? Так что я оставил это вне своего примера...

Следующий пример дает мне постоянные 25 кадров в секунду, даже со случайными задержками в update а также render методы

Теперь, простите, я обычно работаю за миллисекунды, это просто на мой слабый и слабый ум.

import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TestGameLoop {

    public static void main(String[] args) {
        new TestGameLoop();
    }

    public TestGameLoop() {
        gameLoop();
    }

    public static class Constants {

        public static final double OPTIMAL = 25; // fps...

    }

    private boolean game = true;
    private long lastLoop;
    private long lastFps;
    private long fps;

    private void gameLoop() {
        new Thread() {

            private long last;
            private long start;
            private int wait;

            public void run() {

                // Calculate the optimal/maximum delay time
                // This is converted to nanos so it can be 
                // used to calculate the actual delay...
                long millisPerSecond = TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS);
                long optimalDelay =  Math.round(millisPerSecond / Constants.OPTIMAL);

                optimalDelay = TimeUnit.MILLISECONDS.toNanos(optimalDelay);

                // Last start of a "second" loop                    
                long loop = System.nanoTime();
                // While gaming...
                while (game) {
                    // Start of this cycle...
                    long now = System.nanoTime();

                    // Update the state and render the 
                    // current frame...
                    update();
                    render();

                    // How long did that update take??
                    long timeTaken = System.nanoTime();
                    long delta = timeTaken - now;

                    // Subtract the delay from the maximum delay
                    long delay = optimalDelay - delta;
                    if (delay > 0) {
                        try {
                            // Sleep expects milliseconds...
                            delay = TimeUnit.NANOSECONDS.toMillis(delay);
                            Thread.sleep(delay);
                        } catch (InterruptedException ex) {
                            ex.printStackTrace();
                        }
                    }

                    // Calculate if we've being running for a second yet...
                    long loopDelay = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - loop);
                    // If the loop has been cycling for a second...
                    if (loopDelay >= 1) {
                        // Reset the loop time
                        loop = System.nanoTime();
                        System.out.println("FPS = " + fps);
                        fps = 0;
                    } else {
                        // Add another frame to the pile...
                        fps++;
                    }
                }
            }
        }.start();
    }

    public void update() {
        try {
            Thread.sleep(Math.round(Math.random() * 20));
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    public void render() {
        try {
            Thread.sleep(Math.round(Math.random() * 20));
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

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