【问题标题】:Deploying game to server results in strange behaviour将游戏部署到服务器会导致奇怪的行为
【发布时间】:2018-05-24 05:20:39
【问题描述】:

我和一个朋友使用 HTML5 WebSockets 和 java 作为后端开发了一个突破性的类似游戏,最近将我的游戏部署在 Glassfish 服务器上,该服务器在 20 美元的 Digitalocean 液滴(3GB 内存,2cpu)上运行。

在开发游戏时,我与 IntelliJ 以及与 Netbeans 的同事一起工作,在我们的 PC 上运行的 Glassfish 服务器上部署我们的 WAR 文件时,一切都按预期工作。但是当在 droplet 上部署完全相同的 WAR 文件时,球的移动速度似乎是原来的 3 倍。

我尝试通过在虚拟机上安装与 droplet 相同的 Ubuntu 服务器并执行与安装 OpenJDK、Glassfish 时相同的步骤来重现该问题……但在 VM 上它也运行良好。

其他具有 1 个 CPU 的液滴(尝试过 ubuntu 和 centos)会产生相同的问题。我想知道我错过了这个问题的原因可能是什么?

以下是我用于连接/游戏的代码:

WebSocket:

@ServerEndpoint("/singleplayer")
public class SingleplayerSocket {

    private static final Set<Session> PLAYERS = Collections.synchronizedSet(new HashSet<Session>());

    private Session session;
    private Gson gson;
    private Game game;

    private void sendMessage(String message) {
        try {
            for (Session player: PLAYERS) {
                if (player == session) {
                    player.getBasicRemote().sendText(message);
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private void gameStart() {
        game.start();
        sendMessage("Game started");
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        gson = new Gson();
        PLAYERS.add(session);

        sendMessage("Connection established");
    }

    @OnMessage
    public void onMessage(String message) {
        if (session != null && session.isOpen()) {
            String messageType = gson.fromJson(message, MessageType.class).getMessage();

            switch (messageType) {

                case "gameSetup":
                    gameSetup(message);
                    break;

                case "gameStart":
                    gameStart();
                    break;
            }
        }
    }

    @OnClose
    public void onClose(Session session) {
        PLAYERS.remove(session);
        this.session = null;
    }
}

下面有球移动方法的游戏类:

        public class Game implements Runnable {

        private final int TARGET_FPS = 60;
        private final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;

        private volatile boolean gameRunning;
        private volatile boolean gamePaused;

        private Session session;
        private Thread thread;
        private Gson gson;

        public Game(Session session, int width, int height, String difficulty) {
            this.session = session;
            this.WIDTH = width;
            this.HEIGHT = height;
            gson = new Gson();
            timer = new Timer();

            setup(difficulty);
        }

        private void setGameRunning(boolean gameRunning) {
            this.gameRunning = gameRunning;
        }

        private void update(double delta) {
            ball.move(delta);
            collisionDetectionWalls();
            collisionDetectionPaddle();
            collisionDetectionBricks();
        }

        public void start() {
            thread = new Thread(this);
            thread.start();
            setGameRunning(true);
        }

        public void stop() {
            setGameRunning(false);
        }

        private void end(boolean won) {
            updateScore();
            sendGameEnd(won);
            stop();
        }

        private void sendMessage(String message) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void sendGameUpdate() {
            GameUpdateData data = new GameUpdateData(paddle, ball, bricks);
            GameUpdateResponse response = new GameUpdateResponse("gameUpdate", data);
            sendMessage(gson.toJson(response));
        }

        @Override
        public void run() {
            long lastLoopTime = System.nanoTime();
            long lastFpsTime = 0;

            while (gameRunning) {
                long currentTime = System.nanoTime();
                long updateLength = currentTime - lastLoopTime;
                lastLoopTime = currentTime;
                double delta = updateLength / ((double) OPTIMAL_TIME);

                lastFpsTime += updateLength;
                if (lastFpsTime >= 1000000000) {
                    lastFpsTime = 0;
                }

                if (!gamePaused) {
                    update(delta);
                    sendGameUpdate();
                }

                try {
                    long sleepTime = (System.nanoTime() - lastLoopTime + OPTIMAL_TIME) / 1000000;
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                }
            }
        }
    }

public class Ball {
    public void move(double delta) {
        if (isLaunched()){
            double trigoX = Math.cos(angle);
            double trigoY = Math.sin(angle);

            x += trigoX * velocity * delta;
            y += trigoY * velocity * delta;
        }
    }
}

【问题讨论】:

  • 两件事。 1:你忽略你的中断异常。如果 Thread.sleep 碰巧早起,你永远不会知道。 2:你能解释一下为什么你的增量被计算为你从上一帧以来经过的时间的一小部分吗?看起来它是经过时间除以您的目标 FPS(以纳秒为单位)。为什么不一直坚持纳秒呢?我可能遗漏了什么

标签: java jakarta-ee websocket glassfish java-websocket


【解决方案1】:

在开发多人游戏时,我偶然发现了几个问题,并决定使用 Javascript 中的 console.log() 检查几个变量。我注意到单击按钮时游戏启动了两次,并通过添加服务器端检查以防止单击“播放”按钮时多次启动游戏来解决该问题。

private void gameStart() {
    if (!game.isGameRunning()) {
        game.start();
        sendMessage("Game started");
    }
}

球速现在工作正常。

【讨论】:

    【解决方案2】:

    也许你可以试试 System.currentTimeMillis() 因为 System.nanoTime() 不是线程保存。

    参考: Is System.nanoTime() consistent across threads?

    【讨论】:

      猜你喜欢
      • 2013-05-24
      • 2014-02-02
      • 2014-12-31
      • 1970-01-01
      • 2020-07-16
      • 1970-01-01
      • 1970-01-01
      • 2012-02-13
      • 1970-01-01
      相关资源
      最近更新 更多