JavaFX画布双缓冲

6

我正在使用JavaFX使用java.util.Timer和java.util.TimerTask进行游戏循环,并使用JavaFX的Canvas进行渲染来复制经典游戏Pong。是否有一种方法可以将双缓冲区添加到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());
}
2个回答

13

你应该以不同的方式做这件事。

  1. 计时器运行在它自己的线程上,你不需要为此任务添加额外的线程。
  2. 你正在执行对显示画布的修改,而这些修改是在JavaFX应用程序线程之外进行的(你不应该在JavaFX线程之外修改场景中的对象)。
  3. JavaFX具有一个内置的计时器,基于JavaFX系统为每个帧生成的pulse。这个计时器被称为AnimationTimer,你应该使用它。
  4. 你不需要双缓冲。

其他更高级的功能,例如时间轴转换也可以使用,但它们主要用于场景图对象,而您目前正在基于画布进行实现,这不太适合它们。

您可以考虑从使用画布切换到场景图的实现方式,这可能会使实现变得更容易,但您可以任选编码方式。

您不需要双缓冲画布,因为JavaFX架构是延迟绘图架构。您可以在JavaFX应用程序线程上发出绘图命令并调用api来调整场景图,然后完成后,放弃对JavaFX应用程序线程的控制。JavaFX将在内部计算需要呈现的内容,并使用其内部呈现技术发出更新到查看图像,该技术仅绘制完整场景(或修补脏位)。画布内部实现具有命令队列,每个帧都会刷新该队列以渲染对画布的任何更改,因此您不会得到部分更新。
此外,考虑到您拥有类似乒乓球这样基于物理的游戏,您可能希望引入诸如速度之类的概念,将其应用于移动对象(例如球),并在从动画计时器回调的每次迭代中更新对象的位置(此技术在下面的反弹球演示中有展示)。
您可能会对阅读一些资源感兴趣:

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

非常感谢,您的帮助超乎想象。 - silex

3

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

一个很好的例子在这里

谢谢,我想我会使用JavaFX动画,但是我会记住这个选项以备将来的项目。 - silex
@silex jewelsea的回答是非常好的解释..! - GOXR3PLUS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接