JavaFX Canvas Double Buffering

我正在使用JavaFX在Java中复制经典游戏Pong。 我使用java.util.Timer,java.util.TimerTask进行游戏循环,使用JavaFX的Canvas进行渲染。 有没有办法为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()); } 

你应该这样做。

  1. Timer运行自己的线程。 此任务不需要额外的线程。
  2. 您正在从JavaFX应用程序线程执行对显示的canvas的修改(您不应该修改场景中的JavaFX线程中的对象)。
  3. JavaFX具有基于JavaFX系统为每个帧生成的脉冲的内置计时器。 这个计时器叫做AnimationTimer ,你应该使用它。
  4. 你不需要双缓冲。

也可以使用其他更高级别的设施,例如时间轴或过渡 ,但它们主要用于场景图形对象,并且您目前正在将实现基于不适合它们的Canvas 。

您可以考虑将实现从使用canvas切换到场景图,这可能会使实现更容易,但您可以采用任何一种方式对其进行编码。

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

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

您可能有兴趣阅读几个资源:

  • JavaFX中游戏循环的背景信息
  • AnimationTimer的解释
  • 弹跳球演示

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

实现60 fps的最佳方法是使用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(); } 

这里有一个很好的例子。