我正在创建一个基本的太空实时战略游戏,需要一个非常大的地图,但一次只会显示地图的一小部分。我发现使用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。任何建议将不胜感激。
答案 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);
}
}