JavaFX Canvas Double Buffering

时间:2016-04-06 18:41:32

标签: java canvas javafx 2d-games 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());
}

2 个答案:

答案 0 :(得分:8)

你应该采用不同的方式。

  1. Timer运行自己的线程。此任务不需要额外的线程。
  2. 您正在对JavaFX应用程序线程中显示的画布执行修改(您不应该修改JavaFX线程中场景中的对象)。
  3. JavaFX具有基于pulse的内置计时器,该计时器由JavaFX系统为每个帧生成。此计时器称为AnimationTimer,您应该使用它。
  4. 您不需要双重缓冲。
  5. 也可以使用其他更高级别的设施,例如TimelineTransitions,但它们主要用于场景图形对象,而您目前的实施基于Canvas而非Background information on game loops in JavaFX非常适合他们。

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

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

    此外,如果你有一个像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();
    

答案 1 :(得分:0)

达到60 fps的最佳方法是使用AnimationTimer

  1. 您可以通过服装类扩展
  2.  public class AnimationClass extends AnimationTimer {
    
        @Override
        public void handle(long nano) {
          //Code here
        }
    }
    
    1. 您可以使用匿名类立即实现
    2.   new AnimationTimer() {
              @Override
              public void handle(long now) {              
      
              }
          }.start();
      }
      

      一个很好的例子是here