我正在用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中制作这样的东西呢?有什么建议吗?
你没有发布一个最小的、完整的、可验证的例子来说明实际问题是什么,所以你的问题很难完全回答。
我会通过绘制背景(例如在画布上)并将其放在带有移动部件的窗格中(播放器,通过您的描述的声音)来处理这样的事情。然后,通过剪切和平移窗格仅显示部分背景。
这是一个很快的例子;它只是在大画布上放置一些随机的小矩形,然后按光标(箭头)键在场景周围移动一个蓝色矩形(播放器)。主窗格的剪辑和平移绑定到播放器的位置,因此播放器始终显示在中心,除非靠近窗格的边缘。
这需要一点时间来启动,由于某些原因,我有时会看到一个空白屏幕,直到我将播放器移动了几个位置;我没有花太多时间在细节上,所以可能会有一些小错误。
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个扇形面板。增加它会增加更多的扇形,减小它会带走