【问题标题】:Updating UI of TicTacToe in javafx [closed]在 javafx 中更新井字游戏的 UI [关闭]
【发布时间】:2020-04-23 19:48:07
【问题描述】:

这里有一段代码,下面是我从网上收集的,用于理解井字游戏的java代码。我了解代码的编写方式及其逻辑。它在单个窗口中播放,其中两个用户根据单击的鼠标按钮进行区分。现在我想制作一个类似的游戏,可以在由服务器和客户端构建的网络上玩。

井字游戏.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() 上看过一些代码,但不太了解它们。我想要洞察力。

如果有人可以指导我如何为此编写代码并实现我想要的东西以便我可以解决我的问题,那对我将非常有帮助。

谢谢...

【问题讨论】:

  • 对于如何传输数据,您有多种选择。目前最符合行业标准的可能是编写一个简单的类来封装您要传输的数据,然后使用 Java-JSON 库(例如 Jackson)将该类的实例转换为 JSON 格式以发送它,然后收到后将它们转换回来。您也可以对 XML 执行相同的操作,或者只定义您自己的数据字符串表示并使用您自己的代码对其进行解析。在这个站点和其他地方,有很多关于您询问的线程的示例。总的来说,这个问题太模糊/太宽泛了。
  • @James_D 如果可以的话...请给我一些链接。正如我已经说过的,在许多帖子中,我想知道的主题都被简要地讲述了,作为一个新手,我只想要一个我将来可以操纵自己并相应地使用的结构。如您所见,我只想知道如何在服务器和客户端中更新网格。我想我已经清楚地提到了我想知道的(更新 UI)。作为初学者,在 javafx 中从没有代码的文本中理解对我来说很粗糙。任何指导我的代码都是可观的......
  • 我说这个问题太模糊/太宽泛了,因为它不是一个特定的编程问题:它是“这里有几个我想了解的主题”(充其量)。这并不能给出明确、具体的答案。你大概和我一样擅长谷歌搜索,例如“Java Jackson 教程”或“JavaFX 线程示例”。
  • @James_D 好吧...我已经搜索过“在套接字编程中更新 UI”,但我没有看到一个链接为我提供了一个简单的示例,其中我传递了任何对象(文本区域、媒体查看,网格)通过套接字并更新它。我不知所措。我只是想知道如何编写代码来更新这个游戏的窗口。如果可能的话,我也可以在其他情况下使用这个想法。我不能没有 java-jackson 吗?我的教学大纲只围绕基本的 Java、javafx。谢谢
  • 这是作业题吗?

标签: java javafx networking javafx-8


【解决方案1】:

我使用 Platform.RunLater 函数所做的是创建一个后台线程来接收来自客户端/服务器的数据包。然后使用 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);

    }

'''

【讨论】:

  • 它如何更新客户端和服务器的UI?
  • 它只会更新一个,所以客户端和服务器都需要监听数据包并更新。
  • 在我的情况下 getData() 应该做什么?我的意思是它的功能是什么?
  • 您应该已经初始化了通信,即。创建了一个套接字。然后 getData 函数将尝试读取端口以查看是否有任何数据进入。如果您从客户端/服务器接收到包含图形更新值的数据,请在 runlater 函数期间填充图形。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-24
  • 1970-01-01
相关资源
最近更新 更多