javafx快照,性能低下

时间:2016-11-15 02:44:06

标签: java javafx

我正在创建一个基本的太空实时战略游戏,需要一个非常大的地图,但一次只会显示地图的一小部分。我发现使用imageview对视口很容易。不幸的是,imageview需要将图像传递给它,我一直在使用画布将所有精灵绘制到背景上。我一直在使用快照来拍摄完全渲染场景的图像并将其传递给imageview,这样我就可以使用视口,但这会导致性能严重下降。在不使用快照的情况下,游戏以60+ fps运行,但是在使用快照时,fps始终为15或更低。当场景为1024x512时,使用快照不是问题,但场景现在是4096x2048。有没有更好的方法在不使用快照的情况下每秒60次使用imageview和视口?我在下面做的示例代码:

    Group trueRoot = new Group(); //shown image
    Group root = new Group(); //invisible unedited image

    Canvas canvas = new Canvas(4096, 2048); //unedited canvas
    root.getChildren().add(canvas);
    GraphicsContext gc = canvas.getGraphicsContext2D();

    Scene scene2 = new Scene(trueRoot);
    primaryStage.setScene(scene2);

    WritableImage sceneImage = root.snapshot(new SnapshotParameters(), null); //snapshot of fully rendered root scene
    ImageView imageView = new ImageView(sceneImage); //imageView can be used to resize or crop
    Rectangle2D viewportRect = new Rectangle2D(0, 0, 1024, 512); //for cropping
    imageView.setViewport(viewportRect); //for cropping
    trueRoot.getChildren().add(imageView); //also for resizing / cropping

    new AnimationTimer() {
        public void handle(long currentNanoTime) { 

                //simple animated background
                if (cloudTimer == 16384)
                    cloudTimer = 0;
                else
                    cloudTimer++;

                gc.drawImage(stars, 0, 0);
                gc.drawImage(clouds1, cloudTimer % 16384, 0);
                gc.drawImage(clouds1, (cloudTimer % 16384) - 8192, 0);
                gc.drawImage(clouds2, (cloudTimer / 2) % 16384, 0);
                gc.drawImage(clouds2, ((cloudTimer / 2) % 16384) - 8192, 0);

                p1.update(p2, root);
                p2.update(p1, root);
                p1.render(gc, root);
                p2.render(gc, root);

                root.snapshot(new SnapshotParameters(), sceneImage); //takes root scene and hands it to trueRoot
        }
    }.start();


    primaryStage.show();

https://github.com/WiredOverload/ObliterateEverything

的完整代码

我知道我可以使用异步快照来避免在快照发生时延迟其余代码,但这仍然会让游戏保持在15 fps。任何建议将不胜感激。

1 个答案:

答案 0 :(得分:0)

只需使用监听器编写您自己的viewport属性,该监听器可调整GraphicsContext转换以应用转换&适合视口的缩放到Canvas的大小:

@Override
public void start(Stage primaryStage) {
    Canvas canvas = new Canvas();
    GraphicsContext context = canvas.getGraphicsContext2D();

    ObjectProperty<Rectangle2D> viewport = new SimpleObjectProperty<>(Rectangle2D.EMPTY);
    Pane root = new Pane(canvas);
    root.setPrefSize(400, 400);
    canvas.setManaged(false);
    root.layoutBoundsProperty().addListener((observable, oldBound, newBounds) -> {
        canvas.setWidth(newBounds.getWidth());
        canvas.setHeight(newBounds.getHeight());
        Rectangle2D vp = viewport.get();
        viewport.set(new Rectangle2D(vp.getMinX(), vp.getMinY(), newBounds.getWidth(), newBounds.getHeight()));
    });

    viewport.addListener((observable, oldViewport, newViewport) -> {
        // combined transform & scale transfromation
        context.setTransform(canvas.getWidth() / newViewport.getWidth(), 0,
                0, canvas.getHeight() / newViewport.getHeight(),
                -newViewport.getMinX(), -newViewport.getMinY());
        redraw(context);
    });


    // sample movement animation
    Transition scale = new Transition() {

        {
            setCycleDuration(Duration.seconds(2));
            setCycleCount(2);
            setAutoReverse(true);
        }

        @Override
        protected void interpolate(double frac) {
            double scale = 1 + 2 * frac;
            Rectangle2D vp = viewport.get();
            viewport.set(new Rectangle2D(vp.getMinX(), vp.getMinY(), canvas.getWidth() / scale, canvas.getHeight() / scale));
        }

    };

    Transition move = new Transition() {

        {
            setCycleDuration(Duration.seconds(2));
            setCycleCount(2);
            setAutoReverse(true);
        }

        @Override
        protected void interpolate(double frac) {
            double posX = 300 * frac;
            Rectangle2D vp = viewport.get();
            viewport.set(new Rectangle2D(posX, 0, canvas.getWidth(), canvas.getHeight()));
        }

    };

    SequentialTransition animation = new SequentialTransition(scale, move);
    animation.setCycleCount(Animation.INDEFINITE);
    animation.play();

    Scene scene = new Scene(root);

    primaryStage.setScene(scene);
    primaryStage.show();
}

private static Color[] colors = new Color[]{
    Color.RED,
    Color.BEIGE,
    Color.GREEN,
    Color.ORANGE,
    Color.BLACK,
    Color.BLUE,
    Color.WHEAT,
    Color.CORAL,
    Color.CRIMSON,
    Color.PURPLE,
    Color.AQUAMARINE,
    Color.YELLOW,
    Color.CHOCOLATE
};

public static void redraw(GraphicsContext gc) {
    Bounds bounds;
    try {
        bounds = gc.getTransform().inverseTransform(gc.getCanvas().getLayoutBounds());
    } catch (NonInvertibleTransformException ex) {
        throw new IllegalStateException();
    }
    gc.clearRect(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight());
    for (int i = 0; i < colors.length; i++) {
        gc.setFill(colors[i]);
        gc.fillRect(i * 50, 0, 50, 50);
    }
}