JavaFX中的2D摄像机?

JavaFX中的2D摄像机?,javafx,camera,2d,Javafx,Camera,2d,我正在用JavaFX创建一个自上而下的游戏,但是我在实现随玩家移动的摄像头时遇到了问题 我试图创建类似这样的东西,不是移动玩家,而是将场景移动到玩家想要移动的相反方向。这造成了玩家在移动的错觉,但需要场景中所有对象不断移动,这显然造成了大量性能问题。在这之后,我做了一个剪辑,把所有的地形节点放在一个剪辑过的矩形内 下面是我的TerrainRenderer类,它创建了剪切矩形及其内部内容。它所做的是拍摄一幅图像,然后生成一组矩形节点,以便生成一个看起来像图像的贴图 private static f

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

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

下面是我的
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;
}
private static final Pane tileContainer=new Pane();
私有静态最终矩形rectClip=新矩形();
私有静态无效clipChildren(区域){
region.setClip(rectClip);
region.layoutbundsproperty().addListener((ov,oldValue,newValue)->{
setWidth(newValue.getWidth());
setHeight(newValue.getHeight());
});
}
专用静态void drawTile(整数x,整数y,彩色){
最终整块瓷砖尺寸=15;
矩形平铺=新矩形(x*平铺大小,y*平铺大小,平铺大小,平铺大小);
瓷砖.镶嵌填充(颜色);
tileContainer.getChildren().add(平铺);
}
公共静态区域生成(){
最终图像映射=新图像(“main/Images/IcJR6.png”);
对于(int x=0;x<(int)map.getWidth();x++){
对于(int y=0;y<(int)map.getHeight();y++){
drawTile(x,y,map.getPixelReader().getColor(x,y));
}
}
tileContainer.setPrefSize(Main.getAppWidth(),Main.getAppHeight());
儿童(tileContainer);
返回tileContainer;
}
公共静态矩形getRectClip(){
回程夹;
}
下面是我对使用精灵表的播放器的更新方法。到目前为止,这段代码只翻译剪辑节点,而不是其中的内容

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);
    }
}
void update(){
整数速度;
如果(主压力(键代码换档))速度=6;
否则速度=3;
if(Main.isPressed(KeyCode.W)){
getAnimation().play();
getAnimation().setOffsetY(96);
moveY(速度);
}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(速度);
}else getAnimation().stop();
}
@凌驾
受保护的void moveX(int x){
布尔右=x>0;
for(int i=0;i0;
for(int i=0;i

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

您没有发布一个最小的、完整的、可验证的示例来说明实际问题是什么,因此您的问题很难完全回答

我将通过绘制背景(例如,在画布上)并将其放置在带有运动部件的窗格中(播放器,根据您描述的声音)来实现类似的方法。然后通过剪切和平移窗格只显示背景的一部分

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

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

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);
    }
}
import java.util.ArrayList;
导入java.util.List;
导入java.util.Random;
导入javafx.animation.AnimationTimer;
导入javafx.application.application;
导入javafx.beans.binding.Bindings;
导入javafx.scene.scene;
导入javafx.scene.canvas.canvas;
导入javafx.scene.canvas.GraphicsContext;
导入javafx.scene.input.KeyCode;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.Pane;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.stage.stage;
公共类ScrollAndClipBackground扩展了应用程序{
私有最终整数tileSize=10;
私有最终整数numTilesHoriz=500;
私有最终整数numTilesVert=500;
私有最终整数速度=400;//像素/秒
私有布尔向上;
私有布尔下降;
私有布尔左;
私权;
私有最终整数numFilledTiles=numTilesHoriz*numTilesVert/8;
@凌驾
公共无效开始(阶段primaryStage){
Pane=createBackground();
矩形播放器=新矩形(numTilesHoriz*tileSize/2,numTilesVert*tileSize/2,10,10);
player.setFill(颜色:蓝色);
pane.getChildren().add(播放器);
场景=新场景(新边框窗格(pane),800800);
矩形剪辑=新矩形();
clip.widthProperty().bind(scene.widthProperty())