JavaFX 动画 - 细节 Re。如何实施
JavaFX Animation - Specifics Re. How To Implement
我有一些代码可以进行自我回避随机游走:
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.");
}
}
目前,我每点击一次开始按钮,就会进行一次模拟。一个完整的运行的程序,可以这么说。
我的任务是为这个程序制作动画。所以我想在自己的框架中显示每条绘制的线。
我不知道该怎么做。我试过并弄乱了代码。我知道我可能应该使用 KeyFrames 和事件处理程序......不知何故。 (之前做过简单的Animations,但是代码没有这么复杂,没有stepping的必要,或者是用现成的Transition。)
我觉得我的代码有点弱,因为逻辑与 UI 一起。不确定这在实现动画时是否重要。
你能指导我如何去做吗?
您可以像这样将 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();
});
我有一些代码可以进行自我回避随机游走:
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.");
}
}
目前,我每点击一次开始按钮,就会进行一次模拟。一个完整的运行的程序,可以这么说。
我的任务是为这个程序制作动画。所以我想在自己的框架中显示每条绘制的线。
我不知道该怎么做。我试过并弄乱了代码。我知道我可能应该使用 KeyFrames 和事件处理程序......不知何故。 (之前做过简单的Animations,但是代码没有这么复杂,没有stepping的必要,或者是用现成的Transition。)
我觉得我的代码有点弱,因为逻辑与 UI 一起。不确定这在实现动画时是否重要。
你能指导我如何去做吗?
您可以像这样将 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();
});