在javafx中移动屏幕上的对象

在javafx中移动屏幕上的对象,javafx,motion,Javafx,Motion,我是Javafx新手,我正在尝试用它制作一个游戏。 为此,我需要一些物体在屏幕上的流体运动。 我不确定,哪种方法最好。 我用一些矩形启动了一个测试文件。我希望矩形沿路径移动到单击位置。我可以通过设置位置使它出现在那里。所以我想我可以做更小的步,然后运动就会变得流畅。但它不是这样工作的。要么是因为移动太快,所以我需要让进程等待(我想使用线程),要么是因为java intepreter不是sequentiell,因此它只显示最终位置。也许两者都有,或者是我没想到的。 现在我想知道我对这个话题的想法是

我是Javafx新手,我正在尝试用它制作一个游戏。 为此,我需要一些物体在屏幕上的流体运动。 我不确定,哪种方法最好。 我用一些矩形启动了一个测试文件。我希望矩形沿路径移动到单击位置。我可以通过设置位置使它出现在那里。所以我想我可以做更小的步,然后运动就会变得流畅。但它不是这样工作的。要么是因为移动太快,所以我需要让进程等待(我想使用线程),要么是因为java intepreter不是sequentiell,因此它只显示最终位置。也许两者都有,或者是我没想到的。 现在我想知道我对这个话题的想法是否正确,是否有更优雅的方式来实现我的目标。 我希望你能给我一些建议!
关于费利克斯,你需要做的是阅读丹尼尔·希夫曼的汽车游戏,尤其是

这本书很容易理解。您可以将代码应用于JavaFX。我将不详细介绍,您必须自己学习JavaFX。下面是代码:

您需要一个AnimationTimer,在其中应用力,根据力移动对象,并根据对象的位置在UI中显示JavaFX节点

Main.java

package application;

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

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Main extends Application {

    static Random random = new Random();

    Layer playfield;

    List<Attractor> allAttractors = new ArrayList<>();
    List<Vehicle> allVehicles = new ArrayList<>();

    AnimationTimer gameLoop;

    Vector2D mouseLocation = new Vector2D( 0, 0);

    Scene scene;

    MouseGestures mouseGestures = new MouseGestures();

    @Override
    public void start(Stage primaryStage) {

        // create containers
        BorderPane root = new BorderPane();

        // playfield for our Sprites
        playfield = new Layer( Settings.SCENE_WIDTH, Settings.SCENE_HEIGHT);

        // entire game as layers
        Pane layerPane = new Pane();

        layerPane.getChildren().addAll(playfield);

        root.setCenter(layerPane);

        scene = new Scene(root, Settings.SCENE_WIDTH, Settings.SCENE_HEIGHT);

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

        // add content
        prepareGame();

        // add mouse location listener
        addListeners();

        // run animation loop
        startGame();


    }

    private void prepareGame() {

        // add vehicles
        for( int i = 0; i < Settings.VEHICLE_COUNT; i++) {
            addVehicles();
        }

        // add attractors
        for( int i = 0; i < Settings.ATTRACTOR_COUNT; i++) {
            addAttractors();
        }


    }

    private void startGame() {

        // start game
        gameLoop = new AnimationTimer() {

            @Override
            public void handle(long now) {

                // currently we have only 1 attractor
                Attractor attractor = allAttractors.get(0);

                // seek attractor location, apply force to get towards it
                allVehicles.forEach(vehicle -> {

                    vehicle.seek( attractor.getLocation());

                });

                // move sprite
                allVehicles.forEach(Sprite::move);

                // update in fx scene
                allVehicles.forEach(Sprite::display);
                allAttractors.forEach(Sprite::display);

            }
        };

        gameLoop.start();

    }

    /**
     * Add single vehicle to list of vehicles and to the playfield
     */
    private void addVehicles() {

        Layer layer = playfield;

        // random location
        double x = random.nextDouble() * layer.getWidth();
        double y = random.nextDouble() * layer.getHeight();

        // dimensions
        double width = 50;
        double height = width / 2.0;

        // create vehicle data
        Vector2D location = new Vector2D( x,y);
        Vector2D velocity = new Vector2D( 0,0);
        Vector2D acceleration = new Vector2D( 0,0);

        // create sprite and add to layer
        Vehicle vehicle = new Vehicle( layer, location, velocity, acceleration, width, height);

        // register vehicle
        allVehicles.add(vehicle);

    }

    private void addAttractors() {

        Layer layer = playfield;

        // center attractor
        double x = layer.getWidth() / 2;
        double y = layer.getHeight() / 2;

        // dimensions
        double width = 100;
        double height = 100;

        // create attractor data
        Vector2D location = new Vector2D( x,y);
        Vector2D velocity = new Vector2D( 0,0);
        Vector2D acceleration = new Vector2D( 0,0);

        // create attractor and add to layer
        Attractor attractor = new Attractor( layer, location, velocity, acceleration, width, height);

        // register sprite
        allAttractors.add(attractor);

    }

    private void addListeners() {

        // capture mouse position
        scene.addEventFilter(MouseEvent.ANY, e -> {
            mouseLocation.set(e.getX(), e.getY());
        });

        // move attractors via mouse
        for( Attractor attractor: allAttractors) {
            mouseGestures.makeDraggable(attractor);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
演示有一个吸引器,在您的例子中,只需单击鼠标即可。只需单击圆并拖动它。车辆将跟随它

package application;

import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

public class Attractor extends Sprite {

    public Attractor(Layer layer, Vector2D location, Vector2D velocity, Vector2D acceleration, double width, double height) {
        super(layer, location, velocity, acceleration, width, height);
    }

    @Override
    public Node createView() {

        double radius = width / 2;

        Circle circle = new Circle( radius);

        circle.setCenterX(radius);
        circle.setCenterY(radius);

        circle.setStroke(Color.GREEN);
        circle.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.3));

        return circle;
    }

}
以下是用于拖动的代码:

MouseGestures.java

package application;

    import javafx.event.EventHandler;
    import javafx.scene.input.MouseEvent;


    public class MouseGestures {

        final DragContext dragContext = new DragContext();

        public void makeDraggable(final Sprite sprite) {

            sprite.setOnMousePressed(onMousePressedEventHandler);
            sprite.setOnMouseDragged(onMouseDraggedEventHandler);
            sprite.setOnMouseReleased(onMouseReleasedEventHandler);

        }

        EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {

                dragContext.x = event.getSceneX();
                dragContext.y = event.getSceneY();

            }
        };

        EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {

                Sprite sprite = (Sprite) event.getSource();

                double offsetX = event.getSceneX() - dragContext.x;
                double offsetY = event.getSceneY() - dragContext.y;

                sprite.setLocationOffset(offsetX, offsetY);

                dragContext.x = event.getSceneX();
                dragContext.y = event.getSceneY();

            }
        };

        EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {

            }
        };

        class DragContext {

            double x;
            double y;

        }

    }
然后你需要一些课程设置

Settings.java

package application;


public class Settings {

    public static double SCENE_WIDTH = 1280;
    public static double SCENE_HEIGHT = 720;

    public static int ATTRACTOR_COUNT = 1;
    public static int VEHICLE_COUNT = 10;

    public static double SPRITE_MAX_SPEED = 2;
    public static double SPRITE_MAX_FORCE = 0.1;

    // distance at which the sprite moves slower towards the target 
    public static double SPRITE_SLOW_DOWN_DISTANCE = 100; 

}
实用程序类用于创建箭头图像和映射值:

Utils.java

package application;

import javafx.scene.SnapshotParameters;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;


public class Utils {

    public static double map(double value, double currentRangeStart, double currentRangeStop, double targetRangeStart, double targetRangeStop) {
        return targetRangeStart + (targetRangeStop - targetRangeStart) * ((value - currentRangeStart) / (currentRangeStop - currentRangeStart));
    }

    /**
     * Create an imageview of a right facing arrow.
     * @param size The width. The height is calculated as width / 2.0.
     * @param height
     * @return
     */
    public static ImageView createArrowImageView( double size) {

        return createArrowImageView(size, size / 2.0, Color.BLUE, Color.BLUE.deriveColor(1, 1, 1, 0.3), 1);

    }

    /**
     * Create an imageview of a right facing arrow.
     * @param width
     * @param height
     * @return
     */
    public static ImageView createArrowImageView( double width, double height, Paint stroke, Paint fill, double strokeWidth) {

        return new ImageView( createArrowImage(width, height, stroke, fill, strokeWidth));

    }   

    /**
     * Create an image of a right facing arrow.
     * @param width
     * @param height
     * @return
     */
    public static Image createArrowImage( double width, double height, Paint stroke, Paint fill, double strokeWidth) {

        WritableImage wi;

        double arrowWidth = width - strokeWidth * 2;
        double arrowHeight = height - strokeWidth * 2;

        Polygon arrow = new Polygon( 0, 0, arrowWidth, arrowHeight / 2, 0, arrowHeight); // left/right lines of the arrow
        arrow.setStrokeLineJoin(StrokeLineJoin.MITER);
        arrow.setStrokeLineCap(StrokeLineCap.SQUARE);
        arrow.setStroke(stroke);
        arrow.setFill(fill);
        arrow.setStrokeWidth(strokeWidth);

        SnapshotParameters parameters = new SnapshotParameters();
        parameters.setFill(Color.TRANSPARENT); 

        int imageWidth = (int) width;
        int imageHeight = (int) height;

        wi = new WritableImage( imageWidth, imageHeight);
        arrow.snapshot(parameters, wi);

        return wi;

    }

}
当然还有向量计算课

Vector2D.java

package application;



public class Vector2D { 

    public double x;
    public double y;


    public Vector2D(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public void set(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double magnitude() {
        return (double) Math.sqrt(x * x + y * y);
    }

    public void add(Vector2D v) {
        x += v.x;
        y += v.y;
    }

    public void add(double x, double y) {
        this.x += x;
        this.y += y;
    }

    public void multiply(double n) {
        x *= n;
        y *= n;
    }

    public void div(double n) {
        x /= n;
        y /= n;
    }

    public void normalize() {
        double m = magnitude();
        if (m != 0 && m != 1) {
            div(m);
        }
    }

    public void limit(double max) {
        if (magnitude() > max) {
            normalize();
            multiply(max);
        }
    }

    static public Vector2D subtract(Vector2D v1, Vector2D v2) {
        return new Vector2D(v1.x - v2.x, v1.y - v2.y);
    }

    public double heading2D() {
        return Math.atan2(y, x);
    }

}
下面是它的样子


三角形(车辆)将跟随圆圈(吸引子)并在接近圆圈时减速,然后停止。

你的问题有点宽泛,因为关于你要做什么的信息确实不够。如果您只是沿着一条路径移动一个矩形,a就会这样做,一般来说,a就足够了。对于更复杂的游戏和模拟,您可能需要一个Hey thanx作为答案。我会尽量做到更准确:我的目标是进行一场赛车比赛。通过舔橡胶,你可以给你的汽车一个方向和一些不会立即施加的加速度。按下按钮后将执行移动。我试过你建议的方法。我在screne上工作得很好,但是如果我试图得到矩形的位置,它只显示0,0。我需要进一步计算的位置,例如,如果汽车仍在轨道上,当然还有下一个运动。接下来我将检查动画API和AnimationTimer。再次感谢你!哇,罗兰,代码链接的本质是我见过的最好的资源之一。非常感谢…@jewelsea:是的,他解释得很好。我马上就买了。代码可以很容易地映射到JavaFX。他还解释了他频道中关于维米奥的每一章,e。G我在回答中提到的答案。嗨,罗兰,这是一个惊人的答案,肯定会派上用场:)我会坚持这本书。我正在使用MVC模式,所以我将把带有转向力的运动放在modell包中。我想使用绑定将模型汽车与其相应的视图对象连接起来,并为此调整了vector类。不幸的是,每次执行移动时,我都必须绑定。我更愿意将绑定设置为全局绑定,但这到目前为止还不起作用,因为我的initialize方法和move方法是分开的操作。你对此有什么想法吗?我将提出一个关于装订的新问题。还有更多的不起作用。但是谢谢你的帮助!太棒了!不要调整vector类以支持绑定。你需要执行所有的矢量运算,然后移动汽车。如果您真的想使用绑定,您可以做的是创建x和y双重属性,并在display方法i中设置值。E在这里我调用了重定位方法。原因是你想要施加力,然后移动,然后你想要e。G检查目的地是否在屏幕之外。但这一切都必须在汽车在屏幕上移动之前发生。
package application;


public class Settings {

    public static double SCENE_WIDTH = 1280;
    public static double SCENE_HEIGHT = 720;

    public static int ATTRACTOR_COUNT = 1;
    public static int VEHICLE_COUNT = 10;

    public static double SPRITE_MAX_SPEED = 2;
    public static double SPRITE_MAX_FORCE = 0.1;

    // distance at which the sprite moves slower towards the target 
    public static double SPRITE_SLOW_DOWN_DISTANCE = 100; 

}
package application;

import javafx.scene.SnapshotParameters;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;


public class Utils {

    public static double map(double value, double currentRangeStart, double currentRangeStop, double targetRangeStart, double targetRangeStop) {
        return targetRangeStart + (targetRangeStop - targetRangeStart) * ((value - currentRangeStart) / (currentRangeStop - currentRangeStart));
    }

    /**
     * Create an imageview of a right facing arrow.
     * @param size The width. The height is calculated as width / 2.0.
     * @param height
     * @return
     */
    public static ImageView createArrowImageView( double size) {

        return createArrowImageView(size, size / 2.0, Color.BLUE, Color.BLUE.deriveColor(1, 1, 1, 0.3), 1);

    }

    /**
     * Create an imageview of a right facing arrow.
     * @param width
     * @param height
     * @return
     */
    public static ImageView createArrowImageView( double width, double height, Paint stroke, Paint fill, double strokeWidth) {

        return new ImageView( createArrowImage(width, height, stroke, fill, strokeWidth));

    }   

    /**
     * Create an image of a right facing arrow.
     * @param width
     * @param height
     * @return
     */
    public static Image createArrowImage( double width, double height, Paint stroke, Paint fill, double strokeWidth) {

        WritableImage wi;

        double arrowWidth = width - strokeWidth * 2;
        double arrowHeight = height - strokeWidth * 2;

        Polygon arrow = new Polygon( 0, 0, arrowWidth, arrowHeight / 2, 0, arrowHeight); // left/right lines of the arrow
        arrow.setStrokeLineJoin(StrokeLineJoin.MITER);
        arrow.setStrokeLineCap(StrokeLineCap.SQUARE);
        arrow.setStroke(stroke);
        arrow.setFill(fill);
        arrow.setStrokeWidth(strokeWidth);

        SnapshotParameters parameters = new SnapshotParameters();
        parameters.setFill(Color.TRANSPARENT); 

        int imageWidth = (int) width;
        int imageHeight = (int) height;

        wi = new WritableImage( imageWidth, imageHeight);
        arrow.snapshot(parameters, wi);

        return wi;

    }

}
package application;



public class Vector2D { 

    public double x;
    public double y;


    public Vector2D(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public void set(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double magnitude() {
        return (double) Math.sqrt(x * x + y * y);
    }

    public void add(Vector2D v) {
        x += v.x;
        y += v.y;
    }

    public void add(double x, double y) {
        this.x += x;
        this.y += y;
    }

    public void multiply(double n) {
        x *= n;
        y *= n;
    }

    public void div(double n) {
        x /= n;
        y /= n;
    }

    public void normalize() {
        double m = magnitude();
        if (m != 0 && m != 1) {
            div(m);
        }
    }

    public void limit(double max) {
        if (magnitude() > max) {
            normalize();
            multiply(max);
        }
    }

    static public Vector2D subtract(Vector2D v1, Vector2D v2) {
        return new Vector2D(v1.x - v2.x, v1.y - v2.y);
    }

    public double heading2D() {
        return Math.atan2(y, x);
    }

}