JavaFX动画 - 细节Re。如何实施

时间:2022-09-02 15:00:05

I have some code which does a self-avoiding random walk:

我有一些代码可以自我避免随机游走:

package event_handling;

import java.util.ArrayList;
import java.util.Random;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class SelfAvoidingRandomWalk extends Application {

int latticeSize;
int scale;
double initialX, initialY;
double currentX, currentY;
ArrayList<Line> lines;
ArrayList<String> moveDirections;
String chosenDirection;
Pane pane;

public static void main(String[] args) {
    Application.launch(args);
}

@Override
public void start(Stage primaryStage) throws Exception {

    Random random = new Random();
    pane = new Pane();
    Button start = new Button("Start");

    latticeSize = 16;
    scale = 30;

    initialX = latticeSize * scale / 2;
    initialY = latticeSize * scale / 2;

    currentX = initialX;
    currentY = initialY;

    lines = new ArrayList<>();
    moveDirections = new ArrayList<>();

    chosenDirection = "";

    Line[] horizontalGridLines = new Line[latticeSize + 1];
    Line[] verticalGridLines = new Line[latticeSize + 1];

    //Draw gridlines
    for (int a = 0; a < latticeSize + 1; a++) {
        Line l = new Line(0, a * scale, latticeSize * scale, a * scale);
        l.setStroke(Color.LIGHTGRAY);
        horizontalGridLines[a] = l;
    }

    for (int a = 0; a < latticeSize + 1; a++) {
        Line l =  new Line(a * scale, 0, a * scale, latticeSize * scale);
        l.setStroke(Color.LIGHTGRAY);
        verticalGridLines[a] = l;
    }

    pane.getChildren().addAll(horizontalGridLines);
    pane.getChildren().addAll(verticalGridLines);

    BorderPane bPane = new BorderPane();

    bPane.setCenter(pane);
    bPane.setBottom(start);

    BorderPane.setAlignment(start, Pos.CENTER);

    start.setOnMouseClicked(e -> {

        pane.getChildren().removeAll(lines);
        lines.clear();
        initialX = latticeSize * scale / 2;
        initialY = latticeSize * scale / 2;

        currentX = initialX;
        currentY = initialY;

        while (noNodeTouchesBorders() && validMoveExists()) {

            //Check which directions are empty
            buildValidMovesList();

            //Choose from the available directions
            chosenDirection = moveDirections.get(random.nextInt(moveDirections.size()));

            //Make the move
            makeMove();

            //Reset list of possible moves
            moveDirections = new ArrayList<>();

        }

        System.out.println("Finished walk.");

        if (noNodeTouchesBorders()) {
            System.out.println("Dead end.");
        } else {
            System.out.println("Reached exit.");
        }
    });

    Scene scene = new Scene(bPane, latticeSize * scale, latticeSize * scale + 30);

    primaryStage.setTitle("Self-avoiding Random Walk");
    primaryStage.setScene(scene);
    primaryStage.show();

}

private boolean noNodeTouchesBorders() {

    if (currentX == 0 || currentY == 0 || currentX == latticeSize * scale || currentY == latticeSize * scale) {
        return false;
    }

    return true;

}

private boolean validMoveExists() {

    //We have coordinates, and need to check if there are existing lines in the relevant ArrayList with the same endpoint/startpoint.

    boolean blocksUp = false;
    boolean blocksDown = false;
    boolean blocksLeft = false;
    boolean blocksRight = false;

    //For each line,
    for (Line l : lines) {

        //Check if this line blocks in some direction
        if (blocksUp(l)) {
            blocksUp = true;
        }

        if (blocksDown(l)) {
            blocksDown = true;
        }

        if (blocksLeft(l)) {
            blocksLeft = true;
        }

        if (blocksRight(l)) {
            blocksRight = true;
        }

    }

    if (blocksUp && blocksDown && blocksLeft && blocksRight) {
        return false;
    }

    return true;

}

private boolean blocksUp(Line l) {

    if ((l.getStartX() == currentX && l.getStartY() == currentY - scale) || (l.getEndX() == currentX && l.getEndY() == currentY - scale)) {
        return true;
    }

    return false;

}

private boolean blocksDown(Line l) {

    if ((l.getStartX() == currentX && l.getStartY() == currentY + scale) || (l.getEndX() == currentX && l.getEndY() == currentY + scale)) {
        return true;
    }

    return false;

}

private boolean blocksLeft(Line l) {

    if ((l.getStartX() == currentX - scale && l.getStartY() == currentY) || (l.getEndX() == currentX - scale && l.getEndY() == currentY)) {
        return true;
    }

    return false;

}

private boolean blocksRight(Line l) {

    if ((l.getStartX() == currentX + scale && l.getStartY() == currentY) || (l.getEndX() == currentX + scale && l.getEndY() == currentY)) {
        return true;
    }

    return false;

}

private void buildValidMovesList() {

    moveDirections.add("Up");
    moveDirections.add("Down");
    moveDirections.add("Left");
    moveDirections.add("Right");

    for (Line l : lines) {
        if (blocksUp(l)) {
            moveDirections.remove("Up");
        }

        if (blocksDown(l)) {
            moveDirections.remove("Down");
        }

        if (blocksLeft(l)) {
            moveDirections.remove("Left");
        }

        if (blocksRight(l)) {
            moveDirections.remove("Right");
        }
    }

}

private void makeMove() {

    switch (chosenDirection) {
        case "Up" :     moveUp();       break;
        case "Down" :   moveDown();     break;
        case "Left" :   moveLeft();     break;
        case "Right" :  moveRight();    break;
    }

}

private void moveUp() {

    //Create new line
    Line l = new Line(currentX, currentY, currentX, currentY - scale);

    //Add a new line to the lines ArrayList
    lines.add(l);

    //Add the new line to the pane
    pane.getChildren().add(l);

    //Set new currentY
    currentY = currentY - scale;

    System.out.println("Went up.");

}

private void moveDown() {

    //Create new line
    Line l = new Line(currentX, currentY, currentX, currentY + scale);

    //Add a new line to the lines ArrayList
    lines.add(l);

    //Add the new line to the pane
    pane.getChildren().add(l);

    //Set new currentY
    currentY = currentY + scale;

    System.out.println("Went down.");

}

private void moveLeft() {

    //Create new line
    Line l = new Line(currentX, currentY, currentX - scale, currentY);

    //Add a new line to the lines ArrayList
    lines.add(l);

    //Add the new line to the pane
    pane.getChildren().add(l);

    //Set new currentX
    currentX = currentX - scale;

    System.out.println("Went left.");

}

private void moveRight() {

    //Create new line
    Line l = new Line(currentX, currentY, currentX + scale, currentY);

    //Add a new line to the lines ArrayList
    lines.add(l);

    //Add the new line to the pane
    pane.getChildren().add(l);

    //Set new currentX
    currentX = currentX + scale;

    System.out.println("Went right.");

}

}

Currently, every time I click the Start button, one simulation is done. A complete run of the program, so to say.

目前,每次单击“开始”按钮时,都会进行一次模拟。一个完整的程序运行,可以这么说。

My task is to animate this program. So I would like to show each drawn line in its own frame.

我的任务是为该程序制作动画。所以我想在自己的框架中显示每条绘制的线条。

I'm not sure how to go about it. I tried and messed up the code. I understand I should probably be using KeyFrames and event handlers... somehow. (I have done simple Animations before, but the code wasn't this complex. There was no need for stepping, or I was using an existing Transition.)

我不知道该怎么做。我试过并搞砸了代码。我知道我应该使用KeyFrames和事件处理程序......不知何故。 (我以前做过简单的动画,但代码并不复杂。没有必要踩踏,或者我正在使用现有的Transition。)

I perceive that my code is a bit weak since the logic is all together with the UI. Not sure if this matters when implementing the animation.

我认为我的代码有点弱,因为逻辑与UI一起。实现动画时不确定这是否重要。

Could you please guide me on how to go about this?

你能指导我如何解决这个问题吗?

1 个解决方案

#1


You could wrap your while loop inside an AnimationTimer like this:

您可以将while循环包含在AnimationTimer中,如下所示:

start.setOnMouseClicked(e -> {

    pane.getChildren().removeAll(lines);
    lines.clear();
    initialX = latticeSize * scale / 2;
    initialY = latticeSize * scale / 2;

    currentX = initialX;
    currentY = initialY;

    AnimationTimer timer = new AnimationTimer() {

        long prevTime = 0;

        @Override
        public void handle(long now) {

            // some delay
            if ((now - prevTime) < 50_000_000) {
                return;
            }

            prevTime = now;

            if (noNodeTouchesBorders() && validMoveExists()) {

                // Check which directions are empty
                buildValidMovesList();

                // Choose from the available directions
                chosenDirection = moveDirections.get(random.nextInt(moveDirections.size()));

                // Make the move
                makeMove();

                // Reset list of possible moves
                moveDirections = new ArrayList<>();

            } else {

                stop();

                System.out.println("Finished walk.");

                if (noNodeTouchesBorders()) {
                    System.out.println("Dead end.");
                } else {
                    System.out.println("Reached exit.");
                }
            }

        }
    };

    timer.start();

});

#1


You could wrap your while loop inside an AnimationTimer like this:

您可以将while循环包含在AnimationTimer中,如下所示:

start.setOnMouseClicked(e -> {

    pane.getChildren().removeAll(lines);
    lines.clear();
    initialX = latticeSize * scale / 2;
    initialY = latticeSize * scale / 2;

    currentX = initialX;
    currentY = initialY;

    AnimationTimer timer = new AnimationTimer() {

        long prevTime = 0;

        @Override
        public void handle(long now) {

            // some delay
            if ((now - prevTime) < 50_000_000) {
                return;
            }

            prevTime = now;

            if (noNodeTouchesBorders() && validMoveExists()) {

                // Check which directions are empty
                buildValidMovesList();

                // Choose from the available directions
                chosenDirection = moveDirections.get(random.nextInt(moveDirections.size()));

                // Make the move
                makeMove();

                // Reset list of possible moves
                moveDirections = new ArrayList<>();

            } else {

                stop();

                System.out.println("Finished walk.");

                if (noNodeTouchesBorders()) {
                    System.out.println("Dead end.");
                } else {
                    System.out.println("Reached exit.");
                }
            }

        }
    };

    timer.start();

});