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