Двойная буферизация холста JavaFX

Я копирую классическую игру Pong на Java с использованием JavaFX. Я использую java.util.Timer, java.util.TimerTask для игрового цикла и Canvas JavaFX для рендеринга. Есть ли способ добавить двойную буферизацию в Canvas, чтобы анимация не мерцала? Или я должен подойти к этому по-другому? Ниже приведен код. Я удалил некоторые его части, которые я считаю неактуальными, так как код занимает около 200 строк.

Canvas canvas = new Canvas(stageW, stageH);
GraphicsContext gc;

public void start(Stage stage) throws Exception {
    Group root = new Group();

    gc = canvas.getGraphicsContext2D();

    Timer loop = new Timer();

    root.getChildren().add(canvas);

    loop.schedule(new GameLoop(), 0, 1000 / 60);

    stage.setScene(new Scene(root,stageW, stageH));
    stage.show();
}

public class GameLoop extends TimerTask {
    @Override
    public void run() {
        draw(gc);
        collisionDetect();
        ball.move();
    }
}

public void draw() {
    gc.setFill(Color.BLACK);
    gc.fillRect(0, 0, stageW, stageH);

    gc.setFill(Color.WHITE);
    gc.fillRect(lBat.getX(), lBat.getY(), lBat.getW(), lBat.getH());
    gc.fillRect(rBat.getX(), rBat.getY(), rBat.getW(), rBat.getH());
    gc.fillRect(ball.getX(), ball.getY(), ball.getW(), ball.getH());
}

person silex    schedule 06.04.2016    source источник


Ответы (2)


Вы должны сделать это по-другому.

  1. Таймер запускает собственный поток. Вам не нужен дополнительный поток для этой задачи.
  2. Вы выполняете изменения отображаемого холста вне потока приложения JavaFX (вы не должны изменять объекты в сцене вне потока JavaFX).
  3. JavaFX имеет встроенный таймер, основанный на импульс, который генерируется для каждого кадра системой JavaFX. Этот таймер называется AnimationTimer. использовать это.
  4. Вам не нужна двойная буферизация.

Другие средства более высокого уровня, такие как Временная шкала или < можно также использовать href="https://docs.oracle.com/javase/8/javafx/visual-effects-tutorial/basics.htm#BEIIJJJB" rel="noreferrer">переходы, но они в первую очередь предназначены для объектов графа сцены, и в настоящее время ваша реализация основывается на Canvas, который им не подходит.

Вы можете подумать о переключении своей реализации с использования холста на граф сцены, что может сделать реализацию немного проще, но вы можете кодировать ее любым способом.

Вам не нужно дважды буферизовать холст, поскольку архитектура JavaFX представляет собой архитектуру отложенного рисования. Вы вводите команды рисования и вызываете API для настройки графа сцены в потоке приложения JavaFX, затем, когда вы закончите, вы отказываетесь от управления потоком приложения JavaFX. JavaFX будет внутренне обрабатывать то, что необходимо отобразить, и выпускать обновления для просматриваемого изображения, используя свою внутреннюю технологию рендеринга, которая просто рисует полные сцены (или исправляет грязные биты). Внутренняя реализация холста имеет очередь команд, которая сбрасывается для каждого кадра для отображения любых изменений в холсте, поэтому вы не получаете частичных обновлений.

Кроме того, если у вас есть основанная на физике игра, такая как Pong, вы можете ввести такие понятия, как скорость, которую вы применяете к движущимся объектам, таким как мяч, и обновлять положение объекта при каждой итерации обратного вызова из таймера анимации (этот метод продемонстрировано в демонстрации прыгающего мяча ниже).

Возможно, вам будет интересно прочитать пару ресурсов:

Пример кода AnimationTimer (из связанной демонстрации прыгающего мяча):

final LongProperty lastUpdateTime = new SimpleLongProperty(0);
final AnimationTimer timer = new AnimationTimer() {
    @Override
    public void handle(long timestamp) {
        if (lastUpdateTime.get() > 0) {
            long elapsedTime = timestamp - lastUpdateTime.get();
            checkCollisions(ballContainer.getWidth(), ballContainer.getHeight());
            updateWorld(elapsedTime);
            frameStats.addFrame(elapsedTime);
        }
        lastUpdateTime.set(timestamp);
    }
};
timer.start();
person jewelsea    schedule 06.04.2016
comment
Большое спасибо, вы были более чем полезны. - person silex; 06.04.2016

Лучший способ добиться 60 кадров в секунду — использовать AnimationTimer:

  1. Вы можете расширить его с помощью класса костюма

 public class AnimationClass extends AnimationTimer {

    @Override
    public void handle(long nano) {
      //Code here
    }
}
  1. Вы можете мгновенно реализовать это с помощью анонимного класса

  new AnimationTimer() {
        @Override
        public void handle(long now) {              

        }
    }.start();
}

Хороший пример — здесь.

person GOXR3PLUS    schedule 06.04.2016
comment
Спасибо, я думаю, что вместо этого я буду использовать анимацию JavaFX, но я буду помнить об этом для последующих проектов. - person silex; 06.04.2016
comment
Ответ @silex Jewelsea - очень хорошее объяснение ..! - person GOXR3PLUS; 06.04.2016