当前位置: 首页 > 知识库问答 >
问题:

JavaFX中的2D相机?

东方明亮
2023-03-14

我正在用JavaFX创建一个自上而下的游戏,但我在实现随玩家移动的相机时遇到了问题。

我试图创造这样的东西,而不是移动玩家,而是将场景移动到玩家想要的相反方向。这造成了玩家正在移动的错觉,但它需要场景中所有物体的不断移动,这显然会产生大量的性能问题。所以在这之后,我做了一个剪辑,把所有的地形节点放在一个剪辑的矩形内。

下面是我的< code>TerrainRenderer类,它创建了裁剪后的矩形和其中的内容。它所做的是获取一个图像,然后生成一组矩形节点,以制作一个看起来像图像的地图。

private static final Pane tileContainer = new Pane();
private static final Rectangle rectClip = new Rectangle();

private static void clipChildren(Region region) {
    region.setClip(rectClip);
    region.layoutBoundsProperty().addListener((ov, oldValue, newValue) -> {
        rectClip.setWidth(newValue.getWidth());
        rectClip.setHeight(newValue.getHeight());
    });
}

private static void drawTile(int x, int y, Color color) {
    final int TILE_SIZE = 15;
    Rectangle tile = new Rectangle(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
    tile.setFill(color);
    tileContainer.getChildren().add(tile);
}

public static Region generate() {
    final Image map = new Image("main/Images/IcJR6.png");
    for (int x = 0; x < (int) map.getWidth(); x++) {
        for (int y = 0; y < (int) map.getHeight(); y++) {
            drawTile(x, y, map.getPixelReader().getColor(x, y));
        }
    }
    tileContainer.setPrefSize(Main.getAppWidth(), Main.getAppHeight());
    clipChildren(tileContainer);
    return tileContainer;
}

public static Rectangle getRectClip() {
    return rectClip;
}

下面您看到的是我对使用精灵表的播放器的更新方法。到目前为止,这段代码只翻译剪辑节点,而不翻译其中的内容。

void update() {
    int speed;
    if (Main.isPressed(KeyCode.SHIFT)) speed = 6;
    else speed = 3;
    if (Main.isPressed(KeyCode.W)) {
        getAnimation().play();
        getAnimation().setOffsetY(96);
        moveY(speed);
    } else if (Main.isPressed(KeyCode.S)) {
        getAnimation().play();
        getAnimation().setOffsetY(0);
        moveY(-speed);
    } else if (Main.isPressed(KeyCode.D)) {
        getAnimation().play();
        getAnimation().setOffsetY(64);
        moveX(-speed);
    } else if (Main.isPressed(KeyCode.A)) {
        getAnimation().play();
        getAnimation().setOffsetY(32);
        moveX(speed);
    } else getAnimation().stop();
}

@Override
protected void moveX(int x) {
    boolean right = x > 0;
    for(int i = 0; i < Math.abs(x); i++) {
        if (right) TerrainRenderer.getRectClip().setTranslateX(TerrainRenderer.getRectClip().getTranslateX() + 1);
        else TerrainRenderer.getRectClip().setTranslateX(TerrainRenderer.getRectClip().getTranslateX() - 1);
    }
}

@Override
protected void moveY(int y) {
    boolean down = y > 0;
    for (int i = 0; i < Math.abs(y); i++) {
        if (down) TerrainRenderer.getRectClip().setTranslateY(TerrainRenderer.getRectClip().getTranslateY() + 1);
        else TerrainRenderer.getRectClip().setTranslateY(TerrainRenderer.getRectClip().getTranslateY() - 1);
    }
}

我想要的结果看起来像这样(跳到6:10),但是我如何在JavaFX中制作这样的东西呢?有什么建议吗?

共有1个答案

孙福
2023-03-14

你没有发布一个最小的、完整的、可验证的例子来说明实际问题是什么,所以你的问题很难完全回答。

我会通过绘制背景(例如在画布上)并将其放在带有移动部件的窗格中(播放器,通过您的描述的声音)来处理这样的事情。然后,通过剪切和平移窗格仅显示部分背景。

这是一个很快的例子;它只是在大画布上放置一些随机的小矩形,然后按光标(箭头)键在场景周围移动一个蓝色矩形(播放器)。主窗格的剪辑和平移绑定到播放器的位置,因此播放器始终显示在中心,除非靠近窗格的边缘。

这需要一点时间来启动,由于某些原因,我有时会看到一个空白屏幕,直到我将播放器移动了几个位置;我没有花太多时间在细节上,所以可能会有一些小错误。

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class ScrollAndClipBackground extends Application {

    private final int tileSize = 10 ;
    private final int numTilesHoriz = 500 ;
    private final int numTilesVert = 500 ;

    private final int speed = 400 ; // pixels / second
    private boolean up ;
    private boolean down ;
    private boolean left ;
    private boolean right ;

    private final int numFilledTiles = numTilesHoriz * numTilesVert / 8 ;   

    @Override
    public void start(Stage primaryStage) {
        Pane pane = createBackground();
        Rectangle player = new Rectangle(numTilesHoriz*tileSize/2, numTilesVert*tileSize/2, 10, 10);
        player.setFill(Color.BLUE);
        pane.getChildren().add(player);

        Scene scene = new Scene(new BorderPane(pane), 800, 800);
        Rectangle clip = new Rectangle();
        clip.widthProperty().bind(scene.widthProperty());
        clip.heightProperty().bind(scene.heightProperty());

        clip.xProperty().bind(Bindings.createDoubleBinding(
                () -> clampRange(player.getX() - scene.getWidth() / 2, 0, pane.getWidth() - scene.getWidth()), 
                player.xProperty(), scene.widthProperty()));
        clip.yProperty().bind(Bindings.createDoubleBinding(
                () -> clampRange(player.getY() - scene.getHeight() / 2, 0, pane.getHeight() - scene.getHeight()), 
                player.yProperty(), scene.heightProperty()));

        pane.setClip(clip);
        pane.translateXProperty().bind(clip.xProperty().multiply(-1));
        pane.translateYProperty().bind(clip.yProperty().multiply(-1));

        scene.setOnKeyPressed(e -> processKey(e.getCode(), true));
        scene.setOnKeyReleased(e -> processKey(e.getCode(), false));

        AnimationTimer timer = new AnimationTimer() {
            private long lastUpdate = -1 ;
            @Override
            public void handle(long now) {
                long elapsedNanos = now - lastUpdate ;
                if (lastUpdate < 0) {
                    lastUpdate = now ;
                    return ;
                }
                double elapsedSeconds = elapsedNanos / 1_000_000_000.0 ;
                double deltaX = 0 ;
                double deltaY = 0 ;
                if (right) deltaX += speed ;
                if (left) deltaX -= speed ;
                if (down) deltaY += speed ;
                if (up) deltaY -= speed ;
                player.setX(clampRange(player.getX() + deltaX * elapsedSeconds, 0, pane.getWidth() - player.getWidth()));
                player.setY(clampRange(player.getY() + deltaY * elapsedSeconds, 0, pane.getHeight() - player.getHeight()));
                lastUpdate = now ;
            }
        };

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

        timer.start();
    }

    private double clampRange(double value, double min, double max) {
        if (value < min) return min ;
        if (value > max) return max ;
        return value ;
    }

    private void processKey(KeyCode code, boolean on) {
        switch (code) {
        case LEFT: 
            left = on ;
            break ;
        case RIGHT: 
            right = on ;
            break ;
        case UP:
            up = on ;
            break ;
        case DOWN:
            down = on ;
            break ;
        default:
            break ;
        }
    }

    private Pane createBackground() {

        List<Integer> filledTiles = sampleWithoutReplacement(numFilledTiles, numTilesHoriz * numTilesVert);

        Canvas canvas = new Canvas(numTilesHoriz * tileSize, numTilesVert * tileSize);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        gc.setFill(Color.GREEN);

        Pane pane = new Pane(canvas);

        pane.setMinSize(numTilesHoriz * tileSize, numTilesVert * tileSize);
        pane.setPrefSize(numTilesHoriz * tileSize, numTilesVert * tileSize);
        pane.setMaxSize(numTilesHoriz * tileSize, numTilesVert * tileSize);

        for (Integer tile : filledTiles) {
            int x = (tile % numTilesHoriz) * tileSize ;
            int y = (tile / numTilesHoriz) * tileSize ;
            gc.fillRect(x, y, tileSize, tileSize);
        }

        return pane ;
    }

    private List<Integer> sampleWithoutReplacement(int sampleSize, int populationSize) {
        Random rng = new Random();
        List<Integer> population = new ArrayList<>();
        for (int i = 0 ; i < populationSize; i++) 
            population.add(i);
        List<Integer> sample = new ArrayList<>();
        for (int i = 0 ; i < sampleSize ; i++) 
            sample.add(population.remove(rng.nextInt(population.size())));
        return sample;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

一种更复杂的方法是“平铺”机制,它占用的内存更少,主视图由许多平铺组成,这些平铺被移动,并根据需要创建。这更复杂,但允许基本上任意大小的场景。

 类似资料:
  • 如何让我的球从屏幕上的物体上反弹? 下图是一个很好的例子,说明一旦球碰到障碍物,程序应该如何工作。 我让球从墙上反弹,但剩下的是让它也从物体上反弹。谢谢你的帮助! 以下是源代码:

  • 如何使用< code>GridPane上的权重设置行/列大小? 例如,如果我想要五行,其中三行是未知的固定大小,一行占用剩余空间的三分之一,另一行占用剩余空间的三分之二(见下图),我该如何实现呢? 我查看了使用,其中两个有但这只允许平均共享剩余空间。 我尝试使用百分比,因为我在文档< code >中看到,如果widthPercent(或heightPercent)值的总和大于100,这些值将被视为

  • 问题内容: scipy(或另一个流行的库)中是否内置了基于FFT的2D互相关或卷积函数? 有如下功能: -“大数据执行的直接方法将很慢” -“使用精确计算(即不使用FFT)将数组与给定内核相关联。” ,我不太了解,但似乎有误 numarray有一个带switch的函数,但是我想numarray被折叠成numpy了,我找不到是否包含此函数。 问题答案: 我发现,因为还马格努斯指出,但它的时候并没有意

  • 问题内容: 给出两个列表: 我怎么写这样的: 问题答案: 你想要这个吗: 注意:当List处于无序状态时(例如,( 和中的 通知顺序))无用 请参阅此问题以获取更多参考:如何在python中比较列表/集合的列表? 编辑 :感谢@dr jimbob 如果要在排序后进行比较,可以使用。 但是再说一遍,如果那是因为,与众不同( 不是深度排序 ) 为此,您必须使用链接答案中的技术。由于我也在学习Pytho

  • 基本上,我要问的是给定一个正方形2D阵列和一个有效的补丁大小(2D子阵列的大小),我将如何做到这一点。最终,我不需要以任何方式存储子阵列,我只需要找到每个子阵列的中值并将它们存储在一个一维阵列中。中值和存储到新阵列对我来说很简单,我只是不知道如何处理原始2D阵列并正确拆分它。我已经尝试了几次,但一直出现越界错误。我有一个4x4: 我需要像这样拆分它 < code>[1,2] [3,4] [2,3]

  • 问题内容: 我正在尝试扩大以前的任务。它是画一个风扇,并有一个滑块来控制播放速度,并带有播放,暂停和反转风扇的按钮。这是我的作业代码: 我想知道是否有人可以帮助我。我很难让它工作。香港专业教育学院试图制作一个包含多个扇形的hbox,但是它们的尺寸会缩小,而当我调整框的大小时却不会增长。 基本上,我正在努力做到这一点,因此您可以使用一个滑块,最多允许5个扇形面板。增加它会增加更多的扇形,减小它会带走