代码之家  ›  专栏  ›  技术社区  ›  Toni_Entranced

JavaFX Snake Thread.Sleep()不加载FXML

  •  1
  • Toni_Entranced  · 技术社区  · 10 年前

    编辑:找到答案!虽然CAG确实让我走上了正确的道路,所以我会奖励他。不过,正确的答案是由我提供的。

    我正在用画布在JavaFX中制作一个Snake游戏。

    我让游戏在while循环中运行:

    1. 通过设置 正确VBox单元格的背景色。
    2. 等待输入(Thread.sleep(1000))。
    3. 生成下一个视觉效果。

    问题是,如果我使用Thread.sleep(),我的画布根本不会加载。然而,在幕后,游戏仍在运行,直到我撞到墙上死亡。

    我有什么地方做错了吗?thread.sleep()是否暂停了加载和显示JavaFX节点的功能?

        Thread gameThread = new Thread() {
    
            @Override
            public synchronized void start() {
                super.start();
                printGridToGUI();
                while (KEEP_PLAYING) {
                    generateNextGrid();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SnakeGUIController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    Platform.runLater(() -> {
                        printGridToGUI();
                    });
                }
                /*Stop continuing to play. You either won or lost.*/
                if (WON_GAME) {
                    System.out.println("Congratulations!");
                } else {
                    System.out.println("You lose.");
                }
            }
        };
        gameThread.start();
    

    其中printGrid()是:

    /**
     * Prints the grid, with chars in place of on and off areas.
     */
    public void printGridToGUI() {
        resetCanvas();
        for (Coordinate c : coordinates) {
            drawCell(c.row, c.col, true);
        }
        drawCell(food.row, food.col, true);
    }
    

    resetCanvas为:

    /**
     * Clears the boolean array, setting all values to false. A quick way to
     * wipe the grid.
     */
    public final void resetCanvas() {
        /*Lay out the grid on the canvas.*/
        GraphicsContext gc = canvas.getGraphicsContext2D();
        for (int row = 0; row < GRID_SIZE; row++) {
            for (int col = 0; col < GRID_SIZE; col++) {
                drawCell(row, col, false);
            }
        }
    }
    

    drawCell为:

    /**
     * Draws a cell on the canvas at the specified row and col. The row, col
     * coordinates are translated into x,y coordinates for the graphics context.
     *
     * @param row The row of the cell to paint.
     * @param col The col of the cell to paint.     
     * @param cellON The state of the cell, if it is on or off.
     */
    private void drawCell(int row, int col, boolean cellON) {
        /*Translate the row, col value into an x-y cartesian coordinate.*/
        int xCoord = 0 + col * CELL_SIZE;
        int yCoord = 0 + row * CELL_SIZE;
        /*Draw on the canvas.*/
        GraphicsContext gc = canvas.getGraphicsContext2D();
        gc.setFill(Color.BLACK);
        gc.fillRect(xCoord, yCoord, CELL_SIZE, CELL_SIZE);
        if (!cellON) {
            gc.setFill(Color.WHITE);
            int BORDER = 1;
            gc.fillRect(xCoord + BORDER, yCoord + BORDER, CELL_SIZE - BORDER, CELL_SIZE - BORDER);
        }
    }
    
    2 回复  |  直到 10 年前
        1
  •  1
  •   CAG Gonzo    10 年前

    我猜你是在调用 Thread.sleep() 在FX应用程序线程上。该线程负责保持UI响应,因此使其休眠将冻结UI,同时使游戏机制处于响应状态(假设这些是在FX应用程序线程之外执行的)。

    解决方案是在新线程中执行游戏循环,如下所示:

    Thread gameLoop = new Thread(() ->
    {
         while (KEEP_PLAYING)
         {
             printGrid(); //<- I assume this prints the state of the grid to the console, and so is safe to execute off of the FX Application Thread
             try
             {
                 Thread.sleep(1000);
             }
             catch (InterruptedException ex) {}
             Platform.runLater(() ->
             {
                 generateNextGrid(); //<- execute this on the FX Application Thread as it modifies your UI
             });
         }
         if (WON_GAME)
         {
             ...
         }
         else
         {
             ...
         }
    });
    gameLoop.start();
    

    只要您没有在FX应用程序线程上执行任何长时间运行的任务或调用休眠,这将防止任何冻结。

        2
  •  1
  •   Toni_Entranced    10 年前

    想通了!我不得不使用JavaFX的并发包,并使用Task而不是简单的Thread。

        Task<Boolean> gameTask = new Task() {
    
            @Override
            protected Object call() throws Exception {
                while (KEEP_PLAYING) {
                    generateNextGrid();
                    try {
                        Thread.sleep(GAME_SPEED_DELAY);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SnakeGUIController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    Platform.runLater(() -> printGridToGUI());
                }
                /*Stop continuing to play. You either won or lost.*/
                if (WON_GAME) {
                    System.out.println("Congratulations!");
                } else {
                    System.out.println("You lose.");
                }
                return true;
            }
        };
        Thread gameThread = new Thread(gameTask);
        gameThread.start();