【问题标题】:JavaFX Canvas Double BufferingJavaFX 画布双缓冲
【发布时间】:2016-07-27 08:42:09
【问题描述】:

我正在使用 JavaFX 在 Java 中复制经典游戏 Pong。我使用 java.util.Timer、java.util.TimerTask 进行游戏循环和 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());
}

【问题讨论】:

    标签: java canvas javafx 2d-games double-buffering


    【解决方案1】:

    你应该这样做不同。

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

    也可以使用其他更高级别的工具,例如 TimelineTransitions,但它们主要用于场景图对象,您目前的实现基于不太适合它们的 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();
    

    【讨论】:

    • 非常感谢,您的帮助很大。
    【解决方案2】:

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

    一个很好的例子是here

    【讨论】:

    • 谢谢,我想我会改用 JavaFX 动画,但我会在以后的项目中记住这一点。
    • @silex Jewelsea 答案是一个非常好的解释..!
    猜你喜欢
    • 1970-01-01
    • 2011-05-31
    • 2013-11-30
    • 2020-04-01
    • 2014-04-07
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多