【问题标题】:First Person Camera is not on the correct position第一人称相机不在正确的位置
【发布时间】:2022-01-17 10:11:24
【问题描述】:

我正在尝试基于绑定在 JavaFX 中创建第一人称相机。相机和实际位置都可以完美地工作。唯一的问题是它们不匹配!正如您在图片中看到的,实际位置(红色框)在圆圈的中间,但相机在外面。我该如何改变呢?我做错了什么?

Player 类处理 PerspectiveCamera。

package game;

import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.PerspectiveCamera;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.robot.Robot;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

public class Player extends Character {

    private static final Robot ROBOT = new Robot();
    private DoubleProperty relativeCenterX = new SimpleDoubleProperty();
    private DoubleProperty relativeCenterY = new SimpleDoubleProperty();

    protected PerspectiveCamera camera = new PerspectiveCamera();
    protected Rotate xAxis = new Rotate(0, 250, 0, 0, Rotate.Y_AXIS);
    protected Rotate yAxis = new Rotate(0, 0, 250, 0, Rotate.X_AXIS);
    protected Translate translate = new Translate();

    protected DoubleProperty centerX = new SimpleDoubleProperty();
    protected DoubleProperty centerY = new SimpleDoubleProperty();

    @SuppressWarnings("exports")
    public Player(Stage stage) {
        camera.getTransforms().addAll(xAxis, yAxis);

        centerX.bind(stage.widthProperty().divide(2));
        centerY.bind(stage.heightProperty().divide(2));

        relativeCenterX.bind(stage.xProperty().add(centerX));
        relativeCenterY.bind(stage.yProperty().add(centerY));

        camera.translateXProperty().bind(posX.subtract(centerX));
        camera.translateYProperty().bind(posZ);
        camera.translateZProperty().bind(posY.subtract(centerY));

        xAxis.angleProperty().bind(viewX.subtract(90));
        yAxis.angleProperty().bind(viewY);

        translate.xProperty().bind(posX);
        translate.zProperty().bind(posY);
        translate.yProperty().bind(posZ);
    }

    @SuppressWarnings("exports")
    public EventHandler<KeyEvent> getKeyHandle() {
        return e -> {
            switch (e.getCode()) {
            case A:
                view(-1, 0);
                break;
            case D:
                view(1, 0);
                break;
            case W:
                move(1, 1, 0);
                break;
            case S:
                move(-1, -1, 0);
                break;
            case SPACE:
                move(0, 0, 10);
                break;
            case F:
                move(0, 0, -10);
                break;
            default:
                break;
            }
        };
    }

    @SuppressWarnings("exports")
    public EventHandler<MouseEvent> getMouseHandle() {
        return e -> {
            view(e.getSceneX() - centerX.doubleValue(), centerY.doubleValue() - e.getSceneY());
            Platform.runLater(() -> {
                ROBOT.mouseMove(relativeCenterX.intValue(), relativeCenterY.intValue());
            });
        };
    }

    @SuppressWarnings("exports")
    public PerspectiveCamera getPespectiveCamera() {
        return camera;
    }
}

Character 类计算位置和视图。

package game;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public abstract class Character {

    protected DoubleProperty posX = new SimpleDoubleProperty();
    protected DoubleProperty posY = new SimpleDoubleProperty();
    protected DoubleProperty posZ = new SimpleDoubleProperty();

    protected DoubleProperty viewX = new SimpleDoubleProperty();
    protected DoubleProperty viewY = new SimpleDoubleProperty();

    protected DoubleProperty speed = new SimpleDoubleProperty(10);

    public void move(double x, double y, double z) {

        double fX = Math.cos(Math.toRadians(viewX.get()));
        double fY = -Math.sin(Math.toRadians(viewX.get()));
        double fZ = 1;

        posX.set(posX.get() + fX * x * speed.get());
        posY.set(posY.get() + fY * y * speed.get());
        posZ.set(posZ.get() + fZ * z);
    }

    public void view(double x, double y) {
        viewX.set(viewX.get() + x);
        viewY.set(viewY.get() + y);
    }

    @SuppressWarnings("exports")
    public DoubleProperty posXPorperty() {
        return posX;
    }

    @SuppressWarnings("exports")
    public DoubleProperty posYPorperty() {
        return posY;
    }

    @SuppressWarnings("exports")
    public DoubleProperty posZPorperty() {
        return posZ;
    }

    @SuppressWarnings("exports")
    public DoubleProperty viewXPorperty() {
        return viewX;
    }

    @SuppressWarnings("exports")
    public DoubleProperty viewYPorperty() {
        return viewY;
    }
}

我的应用程序,显示全部图形内容。

package graphics;

import game.Player;
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class GameStage extends Application implements Runnable {

    @Override
    public void run() {
        launch();
    }

    @SuppressWarnings("exports")
    @Override
    public void start(Stage stage) throws Exception {

        BorderPane pane = new BorderPane();
        Scene scene = new Scene(pane, 500, 500);

        Group content = new Group(), map = new Group();
        ContentScene subscene = new ContentScene(content, map, 500, 500);
        subscene.widthProperty().bind(scene.widthProperty());
        subscene.heightProperty().bind(scene.heightProperty());
        pane.getChildren().add(subscene);
        pane.setBottom(map);

        Player player = new Player(stage);
        Box box = new Box(50, 50, 50);
        box.translateXProperty().bind(player.posXPorperty());
        box.translateYProperty().bind(player.posZPorperty());
        box.translateZProperty().bind(player.posYPorperty());
        box.rotateProperty().bind(player.viewXPorperty());
        box.setMaterial(new PhongMaterial(Color.RED));
        content.getChildren().add(box);

        Rectangle rectangle = new Rectangle(5, 5);
        rectangle.translateXProperty().bind(player.posXPorperty().divide(10));
        rectangle.translateYProperty().bind(player.posYPorperty().divide(10));
        rectangle.setFill(Color.RED);
        map.getChildren().add(rectangle);

        subscene.setCamera(player.getPespectiveCamera());
        scene.addEventHandler(KeyEvent.KEY_PRESSED, player.getKeyHandle());
        scene.addEventHandler(MouseEvent.MOUSE_MOVED, player.getMouseHandle());
        scene.setFill(Color.BLACK);

        Cursor cursor = Cursor.CROSSHAIR;
        scene.setCursor(cursor);

        stage.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
            if (e.getCode() != KeyCode.F11) {
                return;
            }
            if (stage.isFullScreen()) {
                stage.setFullScreen(false);
            } else {
                stage.setFullScreen(true);
            }
        });

        stage.setAlwaysOnTop(true);
        stage.setScene(scene);
        stage.show();
    }

    private class ContentScene extends SubScene {

        public ContentScene(Group content, Group map, double width, double height) {
            super(content, width, height, true, SceneAntialiasing.BALANCED);

            PhongMaterial material = new PhongMaterial(Color.AQUA);

            for (int v = 0; v < 3_600; v += 180) {
                for (int y = 0; y < 500; y += 100) {
                    Box box = new Box(50, 50, 50);
                    box.setTranslateX(Math.sin(v / 10) * 1_000);
                    box.setTranslateY(y);
                    box.setTranslateZ(Math.cos(v / 10) * 1_000);
                    box.setMaterial(material);
                    content.getChildren().add(box);

                    Rectangle rectangle = new Rectangle(5, 5);
                    rectangle.translateXProperty().bind(box.translateXProperty().divide(10));
                    rectangle.translateYProperty().bind(box.translateZProperty().divide(10));
                    rectangle.setFill(Color.AQUA);
                    map.getChildren().add(rectangle);
                }
            }
        }
    }
}

【问题讨论】:

  • 你是不是故意将 y 绑定到 z 和 z 绑定到 y 以实现红色框的翻译?
  • 是的。这是因为我使用 Z 轴作为高度,而 JavaFX 使用 Z 轴作为深度。为了撤消这个,我扭曲了 y 和 z。
  • 好的,明白了。请注意,这基本上将坐标系从右手(z 指向相机)更改为左手(z 指向远离相机)。为避免混淆,您应该只在一次进行转换(如果有的话)。除此之外,请注意PerspeciveCamera 上的 JavaDoc 的以下部分,这可能与您的问题有关:“如果您要转换(移动)相机,我们建议将 fixedEyeAtCameraZero 设置为 true。当 fixedEyeAtCameraZero 设置为 false 时转换相机可能会导致结果不直观”
  • 另一件让我感到奇怪的事情:camera.translateXProperty().bind(posX.subtract(centerX)); - 摄像机的位置不应该与玩家的位置相同并且不被舞台中心偏移吗?
  • fixedEyeAtCameraZero zu setzen hat erstaunlich viel bewirkt。 Vielen Dank!

标签: java javafx perspectivecamera


【解决方案1】:

感谢 Thomas,我能够解决问题。代码现在看起来像这样:

package graphics;

import game.Player;
import javafx.application.Application;
import javafx.scene.AmbientLight;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class GameStage extends Application implements Runnable {

    @Override
    public void run() {
        launch();
    }

    @SuppressWarnings("exports")
    @Override
    public void start(Stage stage) throws Exception {

        // Parents
        BorderPane pane = new BorderPane();
        Group content = new Group(new AmbientLight()), map = new Group();
        Pane stackpane = new Pane(map);

        // Scenes
        Scene scene = new Scene(pane, 500, 500);
        SubScene contentSubscene = new SubScene(content, 500, 500, true, SceneAntialiasing.BALANCED);
        contentSubscene.widthProperty().bind(scene.widthProperty());
        contentSubscene.heightProperty().bind(scene.heightProperty());
        SubScene minimapSubscene = new SubScene(stackpane, 256, 256);
        minimapSubscene.setFill(Color.DARKGREY);

        pane.getChildren().add(contentSubscene);
        pane.setBottom(minimapSubscene);

        // Create Player
        Player player = new Player(stage);
        Rectangle currentPosition = new Rectangle(5, 5);
        currentPosition.layoutXProperty().bind(minimapSubscene.widthProperty().divide(2));
        currentPosition.layoutYProperty().bind(minimapSubscene.heightProperty().divide(2));
        currentPosition.setFill(Color.RED);
        stackpane.getChildren().add(currentPosition);

        map.layoutXProperty().bind(player.posXPorperty().divide(-10).add(minimapSubscene.widthProperty().divide(2)));
        map.layoutYProperty().bind(player.posYPorperty().divide(-10).add(minimapSubscene.heightProperty().divide(2)));

        // Create Box in
        PhongMaterial material = new PhongMaterial(Color.AQUA);
        for (int v = 0; v < 3_600; v += 180) {
            for (int y = 0; y < 500; y += 100) {
                Box box = new Box(50, 50, 50);
                box.setTranslateX(Math.sin(v / 10) * 1_000);
                box.setTranslateY(y);
                box.setTranslateZ(Math.cos(v / 10) * 1_000);
                box.setMaterial(material);
                content.getChildren().add(box);

                Rectangle boxPosition = new Rectangle(5, 5);
                boxPosition.translateXProperty().bind(box.translateXProperty().divide(10));
                boxPosition.translateYProperty().bind(box.translateZProperty().divide(10));
                boxPosition.setFill(Color.AQUA);
                map.getChildren().add(boxPosition);
            }
        }

        contentSubscene.setCamera(player.getPespectiveCamera());
        scene.addEventHandler(KeyEvent.KEY_PRESSED, player.getKeyHandle());
        scene.addEventHandler(MouseEvent.MOUSE_MOVED, player.getMouseHandle());
        scene.setCursor(Cursor.CROSSHAIR);
        scene.setFill(Color.WHITE);

        stage.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
            if (e.getCode() != KeyCode.F11) {
                return;
            }
            if (stage.isFullScreen()) {
                stage.setFullScreen(false);
            } else {
                stage.setFullScreen(true);
            }
        });
        stage.setAlwaysOnTop(true);
        stage.setScene(scene);
        stage.show();

    }
}
package game;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public abstract class Character {

    protected DoubleProperty posX = new SimpleDoubleProperty();
    protected DoubleProperty posY = new SimpleDoubleProperty();
    protected DoubleProperty posZ = new SimpleDoubleProperty();

    protected DoubleProperty viewX = new SimpleDoubleProperty();
    protected DoubleProperty viewY = new SimpleDoubleProperty();

    protected DoubleProperty speed = new SimpleDoubleProperty(10);

    public void move(double x, double y, double z) {

        double fX = Math.cos(Math.toRadians(viewX.get()));
        double fY = -Math.sin(Math.toRadians(viewX.get()));
        double fZ = 1;

        posX.set(posX.get() + fX * x * speed.get());
        posY.set(posY.get() + fY * y * speed.get());
        posZ.set(posZ.get() + fZ * z);
    }

    public void view(double x, double y) {
        viewX.set(viewX.get() + x);
        viewY.set(viewY.get() + y);
    }

    @SuppressWarnings("exports")
    public DoubleProperty posXPorperty() {
        return posX;
    }

    @SuppressWarnings("exports")
    public DoubleProperty posYPorperty() {
        return posY;
    }

    @SuppressWarnings("exports")
    public DoubleProperty posZPorperty() {
        return posZ;
    }

    @SuppressWarnings("exports")
    public DoubleProperty viewXPorperty() {
        return viewX;
    }

    @SuppressWarnings("exports")
    public DoubleProperty viewYPorperty() {
        return viewY;
    }
}
package game;

import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.PerspectiveCamera;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.robot.Robot;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;

public class Player extends Character {

    private static final Robot ROBOT = new Robot();
    private DoubleProperty relativeCenterX = new SimpleDoubleProperty();
    private DoubleProperty relativeCenterY = new SimpleDoubleProperty();

    protected PerspectiveCamera camera = new PerspectiveCamera(true);
    protected Rotate xAxis = new Rotate(0, 250, 0, 0, Rotate.Y_AXIS);
    protected Rotate yAxis = new Rotate(0, 0, 250, 0, Rotate.X_AXIS);

    protected DoubleProperty centerX = new SimpleDoubleProperty();
    protected DoubleProperty centerY = new SimpleDoubleProperty();

    @SuppressWarnings("exports")
    public Player(Stage stage) {
        camera.getTransforms().addAll(xAxis, yAxis);
        camera.setFieldOfView((40 + 62) / 2);
        camera.setNearClip(0.1);
        camera.setFarClip(100000);
        camera.setVerticalFieldOfView(true);

        centerX.bind(stage.widthProperty().divide(2));
        centerY.bind(stage.heightProperty().divide(2));

        relativeCenterX.bind(stage.xProperty().add(centerX));
        relativeCenterY.bind(stage.yProperty().add(centerY));

        xAxis.angleProperty().bind(viewX.subtract(90));
        yAxis.angleProperty().bind(viewY);

        camera.translateXProperty().bind(posX);
        camera.translateZProperty().bind(posY);
        camera.translateYProperty().bind(posZ);
    }

    @SuppressWarnings("exports")
    public EventHandler<KeyEvent> getKeyHandle() {
        return e -> {
            switch (e.getCode()) {
            case A:
                view(-1, 0);
                break;
            case D:
                view(1, 0);
                break;
            case W:
                move(1, 1, 0);
                break;
            case S:
                move(-1, -1, 0);
                break;
            case SPACE:
                move(0, 0, 10);
                break;
            case F:
                move(0, 0, -10);
                break;
            default:
                break;
            }
        };
    }

    @SuppressWarnings("exports")
    public EventHandler<MouseEvent> getMouseHandle() {
        return e -> {
            view(e.getSceneX() - centerX.doubleValue(), centerY.doubleValue() - e.getSceneY());
            Platform.runLater(() -> {
                ROBOT.mouseMove(relativeCenterX.intValue(), relativeCenterY.intValue());
            });
        };
    }

    @SuppressWarnings("exports")
    public PerspectiveCamera getPespectiveCamera() {
        return camera;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多