JavaFX Canvas Double Buffering

问题描述:

我正在使用JavaFX在Java中复制经典游戏Pong。我使用java.util.Timer,java.util.TimerTask进行游戏循环,使用JavaFX的Canvas进行渲染。有没有办法为Canvas添加双缓冲,以便动画不会闪烁?或者我应该采用不同的方法吗?贝娄是代码。我删除了它的一些部分,我认为这些部分无关紧要,因为代码长约200行。

I am replicating a classic game, Pong, in Java using JavaFX. I am using java.util.Timer, java.util.TimerTask for the game loop and JavaFX's Canvas for rendering. Is there a way to add double buffering to the Canvas so the animation doesn't flicker? Or should I approach this differently? Bellow is the code. I removed some parts of it, that I think are irrelevant, since the code is around 200 lines long.

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());
}


您应该采取不同的方式。

You should do this differently.


  1. Timer运行自己的线程。您不需要额外的线程来完成此任务。

  2. 您正在对JavaFX应用程序线程中显示的画布执行修改(您不应该修改JavaFX场景中的对象)线程)。

  3. JavaFX有一个基于 pulse 。此计时器称为 AnimationTimer ,您应该使用它。

  4. 你不需要双缓冲。

  1. Timer runs its own thread. You don't need an additional thread for this task.
  2. You are executing modifications to the displayed canvas off of the JavaFX application thread (you should not modify objects in the scene off of the JavaFX thread).
  3. JavaFX has an in-built timer based upon a pulse that is generated for each frame by the JavaFX system. This timer is called an AnimationTimer, you should use that.
  4. You don't need double buffering.

其他更高级别的设施如时间轴也可以使用过渡,但它们主要用于场景图对象,您目前正在画布上实现您的实现它不适合它们。

Other higher level facilities such as Timeline or Transitions could also be used, but they are primarily for scene graph objects and you are currently basing your implementation on a Canvas which is not well suited to them.

您可以考虑将实现从使用画布切换到场景图,这可能会使实现更容易一些,但你可以用任何一种方式编码。

You could consider switching your implementation from using canvas to the scene graph, which might make the implementation a bit easier, but you can code it either way.

您不需要对画布进行双缓冲,因为JavaFX体系结构是延迟绘制体系结构。您发出绘图命令并调用api来调整JavaFX应用程序线程上的场景图,然后,当您完成后,您放弃对JavaFX应用程序线程的控制。 JavaFX将在内部解决需要渲染的内容,并使用内部渲染技术对查看的图像发布更新,该技术仅绘制完整的场景(或修补脏位)。 canvas内部实现有一个命令队列,为每个帧刷新以呈现画布的任何更改,因此您不会获得部分更新。

You don't need to double-buffer the canvas as the JavaFX architecture is a delayed drawing architecture. You issue drawing commands and invoke api to adjust the scene graph on the JavaFX application thread, then, when you are done, you relinquish control of the JavaFX application thread. JavaFX will work out internally what needs to be rendered and issue updates to the viewed image using it's internal rendering technology, which just draws complete scenes (or patches the dirty bits). The canvas internal implementation has a command queue which is flushed for each frame to render any changes to the canvas, so you don't get partial updates.

此外,给你有一个像Pong这样的基于物理的游戏,你可能想要引入一些概念,比如应用于移动物体(如球)的速度,并在动画计时器的回调的每次迭代中更新物体位置(这种技术在弹跳中得到证明)球演示如下)。

Additionally, given you have a physics based game like Pong, you might want to introduce concepts such as velocity that you apply to moving objects such as the ball and update the object position on each iteration of the callback from the animation timer (this technique is demonstrated in the bouncing ball demo below).

您可能有兴趣阅读一些资源:

You may be interested in reading a couple of resources:

  • Background information on game loops in JavaFX
  • Explanation of the AnimationTimer
  • Bouncing Ball Demo

示例AnimationTimer代码(来自弹跳球演示链接):

Sample AnimationTimer code (from the bouncing ball demo linked):

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();