JavaFX实时套接字多人游戏-如何从不同线程更新GUI并设置场景

时间:2019-04-03 19:12:43

标签: java multithreading user-interface javafx javafx-11

我正在尝试使用Java和JavaFX创建多人“四人乒乓”。网络部分运行良好。服务器处理所有游戏逻辑,并将球的位置和所有球员的位置发送给所有四个客户端。

现在,我需要向客户端添加GUI。但是,问题从这里开始。 我需要了解两件事:如何在不冻结GUI的情况下从不同的线程更新JavaFX节点(例如,每次服务器发送新坐标时都设置球位置),以及如何告诉GUI再次从另一个位置设置GUI不同的线程?

客户端的体系结构如下:我有一个客户端类,它处理所有网络和协议内容,主要是发送到服务器和从服务器接收。

我考虑过将JavaFX GUI类设置为客户端的入口点,然后在其中创建一个新的客户端对象。然后,GUI可以调用客户端对象上的方法,例如client.move(DIRECTION);client.sendMessage("Hello")。那不是问题,但是我找不到针对客户端-> GUI的另一种好的解决方案。

客户端必须以某种方式能够修改节点,例如球。当服务器发送新的BALL_POSITION x y命令时,客户端必须以某种方式告诉GUI新的球位置。我不能使用Platform.runLater(Runnable);,因为如果球位置每100毫秒左右更新一次,则GUI冻结。对于球员的位置,这是相同的。

使用SimpleIntegerProperty对球的位置有效。我在GUI中为此属性添加了一个侦听器,然后在不冻结GUI的情况下对其进行了更新。 那是正确的方法还是我应该完全不同?

又如何告诉GUI在客户端内部切换场景,而该客户端在与GUI不同的线程中运行?

我尝试的控球位置(简化):

Client.java

class Client extends Thread {
  private Ball ball;

  Client() {
    this.ball = new Ball();
    new Thread(ball).start();
  }

  Ball getBall() {
    return this.ball;
  }
}

Ball.java

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

class Ball implements Runnable {
  private IntegerProperty ballX;
  private IntegerProperty ballY;

  Ball() {
    ballX = new SimpleIntegerProperty(this, "ballX", 0);
    ballY = new SimpleIntegerProperty(this, "ballY", 0);
  }

  public void setPosition(int x, int y) {
     this.ballX.setValue(x);
     this.ballX.setValue(y);
  }

  IntegerProperty getBallX() {
    return this.ballX;
  }

  IntegerProperty getBallY() {
    return this.ballY;
  }
}

Protocol.java

switch(command) {

   case(BALL_POSITION):
      ...
      client.getBall().setPosition(x, y);

}

GameGUI.java

Circle circle = new Circle(0, 0, 30);
final Client client = new Client();
...
final AtomicInteger count1 = new AtomicInteger(-1);
client.getBall().getBallX().addListener(new ChangeListener<Number>() {
   @Override
   public void changed(final ObservableValue<? extends Number> observable,
        final Number oldValue, final Number newValue) {
            if (count1.getAndSet(newValue.intValue()) == -1) {
               Platform.runLater(new Runnable() {
                  @Override
                  public void run() {
                     int value = count1.getAndSet(-1);
                     ballXLabel.setText("BallX: "  + value);
                     circle.setCenterX(value);
                    }
                 });
              }
          }
      });

这在开始时就可以了,但是我真的不知道这是否应该这样做。 另外,如果服务器发送命令,例如GAME_END,我将如何切换场景?

我真的希望有人愿意帮助我,谢谢!!

0 个答案:

没有答案