正在更新 javafx 中的 TicTacToe UI
Updating UI of TicTacToe in javafx
这里有一段代码是为了理解java中的TicTacToe游戏代码从网上收集的。我了解代码的编写方式及其逻辑。它在单个 window 中播放,其中两个用户根据单击的鼠标按钮进行区分。现在我想做一个类似的游戏,可以在由服务器和客户端构建的网络上玩。
TicTacToe.java
package sample;
import java.util.ArrayList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private boolean playable = true;
private boolean turnX = true;
private Tile[][] board = new Tile[3][3];
private List<Combo> combos = new ArrayList<>();
private Pane root = new Pane();
private Parent createContent() {
root.setPrefSize(600, 600);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Tile tile = new Tile(); //each tile is a rectangular block
tile.setTranslateX(j * 200);
tile.setTranslateY(i * 200);
root.getChildren().add(tile);
board[j][i] = tile;
}
}
// horizontal
for (int y = 0; y < 3; y++) {
combos.add(new Combo(board[0][y], board[1][y], board[2][y]));
}
// vertical
for (int x = 0; x < 3; x++) {
combos.add(new Combo(board[x][0], board[x][1], board[x][2]));
}
// diagonals
combos.add(new Combo(board[0][0], board[1][1], board[2][2]));
combos.add(new Combo(board[2][0], board[1][1], board[0][2]));
return root;
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private void checkState() {
for (Combo combo : combos) {
if (combo.isComplete()) {
playable = false;
playWinAnimation(combo);
break;
}
}
}
private void playWinAnimation(Combo combo) {
Line line = new Line();
line.setStartX(combo.tiles[0].getCenterX());
line.setStartY(combo.tiles[0].getCenterY());
line.setEndX(combo.tiles[0].getCenterX());
line.setEndY(combo.tiles[0].getCenterY());
root.getChildren().add(line);
Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(1),
new KeyValue(line.endXProperty(), combo.tiles[2].getCenterX()),
new KeyValue(line.endYProperty(), combo.tiles[2].getCenterY())));
timeline.play();
}
private class Combo {
private Tile[] tiles;
public Combo(Tile... tiles) {
this.tiles = tiles;
}
public boolean isComplete() {
if (tiles[0].getValue().isEmpty())
return false;
return tiles[0].getValue().equals(tiles[1].getValue())
&& tiles[0].getValue().equals(tiles[2].getValue());
}
}
private class Tile extends StackPane {
private Text text = new Text();
private boolean clicked = false;;
public Tile() {
Rectangle border = new Rectangle(200, 200);
border.setFill(null);
border.setStroke(Color.BLACK);
text.setFont(Font.font(72));
setAlignment(Pos.CENTER);
getChildren().addAll(border, text);
setOnMouseClicked(event -> {
if (!playable || clicked)
return;
if (event.getButton() == MouseButton.PRIMARY) {
if (!turnX)
return;
if(!clicked){
drawX();
turnX = false;
clicked = true;
checkState();
}
}
else if (event.getButton() == MouseButton.SECONDARY) {
if (turnX)
return;
if(!clicked){
drawO();
turnX = true;
clicked = true;
checkState();
}
}
});
}
public double getCenterX() {
return getTranslateX() + 100;
}
public double getCenterY() {
return getTranslateY() + 100;
}
public String getValue() {
return text.getText();
}
private void drawX() {
text.setText("X");
}
private void drawO() {
text.setText("O");
}
}
public static void main(String[] args) {
launch(args);
}
}
我想在不同的文件中构建服务器和客户端。 server.java 将包含服务器套接字、输入输出流,并接受来自套接字的连接。对于那件事我没有任何疑问。同样,client.java 也将有套接字和必要的流。服务器文件和客户端文件也将具有 TicTacToe.java。我可以设置代码到此为止。
我的问题:
我无法通过套接字网络更新 UI。我该怎么做?我看过一些关于 Platform.runLater() 的代码,但不太理解它们。我想要洞察力。
如果有人可以指导我如何为此编写代码并实现我想要的,以便我可以解决我的问题,那将对我非常有帮助。
谢谢...
为了使用 Platform.RunLater 函数,我所做的是创建一个后台线程来接收来自 client/server 的数据包。然后使用 platform.runLater 函数更新 UI...
private ScheduledExecutorService scheduledPINGExecutorService;
private ScheduledFuture<?> scheduledPINGCheck;
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage){
primaryStage.setTitle("Title");
primaryStage.setScene(new Scene(getMainPage(primaryStage), 800, 1000));
primaryStage.show();
scheduledPINGExecutorService = Executors.newScheduledThreadPool(1);
Runnable checkPING = () -> {
//check for new packet from client
getData() <---- define this function depending on your networking setup
Platform.runLater(() -> { //populate UI with new data });
};
scheduledPINGCheck = scheduledPINGExecutorService.scheduleAtFixedRate(checkPING, 1, 500, TimeUnit.MILLISECONDS);
}
'''
这里有一段代码是为了理解java中的TicTacToe游戏代码从网上收集的。我了解代码的编写方式及其逻辑。它在单个 window 中播放,其中两个用户根据单击的鼠标按钮进行区分。现在我想做一个类似的游戏,可以在由服务器和客户端构建的网络上玩。
TicTacToe.java
package sample;
import java.util.ArrayList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private boolean playable = true;
private boolean turnX = true;
private Tile[][] board = new Tile[3][3];
private List<Combo> combos = new ArrayList<>();
private Pane root = new Pane();
private Parent createContent() {
root.setPrefSize(600, 600);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Tile tile = new Tile(); //each tile is a rectangular block
tile.setTranslateX(j * 200);
tile.setTranslateY(i * 200);
root.getChildren().add(tile);
board[j][i] = tile;
}
}
// horizontal
for (int y = 0; y < 3; y++) {
combos.add(new Combo(board[0][y], board[1][y], board[2][y]));
}
// vertical
for (int x = 0; x < 3; x++) {
combos.add(new Combo(board[x][0], board[x][1], board[x][2]));
}
// diagonals
combos.add(new Combo(board[0][0], board[1][1], board[2][2]));
combos.add(new Combo(board[2][0], board[1][1], board[0][2]));
return root;
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private void checkState() {
for (Combo combo : combos) {
if (combo.isComplete()) {
playable = false;
playWinAnimation(combo);
break;
}
}
}
private void playWinAnimation(Combo combo) {
Line line = new Line();
line.setStartX(combo.tiles[0].getCenterX());
line.setStartY(combo.tiles[0].getCenterY());
line.setEndX(combo.tiles[0].getCenterX());
line.setEndY(combo.tiles[0].getCenterY());
root.getChildren().add(line);
Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(1),
new KeyValue(line.endXProperty(), combo.tiles[2].getCenterX()),
new KeyValue(line.endYProperty(), combo.tiles[2].getCenterY())));
timeline.play();
}
private class Combo {
private Tile[] tiles;
public Combo(Tile... tiles) {
this.tiles = tiles;
}
public boolean isComplete() {
if (tiles[0].getValue().isEmpty())
return false;
return tiles[0].getValue().equals(tiles[1].getValue())
&& tiles[0].getValue().equals(tiles[2].getValue());
}
}
private class Tile extends StackPane {
private Text text = new Text();
private boolean clicked = false;;
public Tile() {
Rectangle border = new Rectangle(200, 200);
border.setFill(null);
border.setStroke(Color.BLACK);
text.setFont(Font.font(72));
setAlignment(Pos.CENTER);
getChildren().addAll(border, text);
setOnMouseClicked(event -> {
if (!playable || clicked)
return;
if (event.getButton() == MouseButton.PRIMARY) {
if (!turnX)
return;
if(!clicked){
drawX();
turnX = false;
clicked = true;
checkState();
}
}
else if (event.getButton() == MouseButton.SECONDARY) {
if (turnX)
return;
if(!clicked){
drawO();
turnX = true;
clicked = true;
checkState();
}
}
});
}
public double getCenterX() {
return getTranslateX() + 100;
}
public double getCenterY() {
return getTranslateY() + 100;
}
public String getValue() {
return text.getText();
}
private void drawX() {
text.setText("X");
}
private void drawO() {
text.setText("O");
}
}
public static void main(String[] args) {
launch(args);
}
}
我想在不同的文件中构建服务器和客户端。 server.java 将包含服务器套接字、输入输出流,并接受来自套接字的连接。对于那件事我没有任何疑问。同样,client.java 也将有套接字和必要的流。服务器文件和客户端文件也将具有 TicTacToe.java。我可以设置代码到此为止。
我的问题:
我无法通过套接字网络更新 UI。我该怎么做?我看过一些关于 Platform.runLater() 的代码,但不太理解它们。我想要洞察力。
如果有人可以指导我如何为此编写代码并实现我想要的,以便我可以解决我的问题,那将对我非常有帮助。
谢谢...
为了使用 Platform.RunLater 函数,我所做的是创建一个后台线程来接收来自 client/server 的数据包。然后使用 platform.runLater 函数更新 UI...
private ScheduledExecutorService scheduledPINGExecutorService;
private ScheduledFuture<?> scheduledPINGCheck;
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage){
primaryStage.setTitle("Title");
primaryStage.setScene(new Scene(getMainPage(primaryStage), 800, 1000));
primaryStage.show();
scheduledPINGExecutorService = Executors.newScheduledThreadPool(1);
Runnable checkPING = () -> {
//check for new packet from client
getData() <---- define this function depending on your networking setup
Platform.runLater(() -> { //populate UI with new data });
};
scheduledPINGCheck = scheduledPINGExecutorService.scheduleAtFixedRate(checkPING, 1, 500, TimeUnit.MILLISECONDS);
}
'''