JavaFX Canvas 双缓冲

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

最佳答案

您应该以不同的方式执行此操作。

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

其他更高级别的设施,例如 TimelineTransitions也可以使用,但它们主要用于场景图对象,并且您当前的实现基于 Canvas这不太适合他们。

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

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

此外,假设您有一个像 Pong 这样基于物理的游戏,您可能需要引入诸如速度之类的概念,将其应用于移动对象(例如球),并在动画计时器回调的每次迭代中更新对象位置(该技术在下面的弹跳球演示中进行了演示)。

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

示例动画计时器代码(来自链接的弹跳球演示):

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

关于JavaFX Canvas 双缓冲,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36459710/

相关文章:

java - 如何以编程方式获取 Java 中枚举的引用?

javascript - HTML5 Canvas - 以特定 Angular 在圆内绘制一条线

jquery - CSS 3D 将 div 转换为 flex 的拱形

java - 一列添加两个按钮并获取点击行值javafx

Java 集合和内存使用

java - 无法在三角形的相邻边之间绘制圆弧

java - 他们的 hashCode 返回的确切值意味着什么?

canvas - chrome扩展 Canvas 不在循环内绘制图像

java - 根据 String.valueOf(Object obj) 的值创建一个新 Shape

java - JavaFX 场景中的多个按键