简体   繁体   中英

Java, countdown timer for a 15 puzzle game? Help me understand Timers, please

Before I start I want to explain that I've looked at many other questions like this and many videos on the net but none of them help me understand how timer works or how I'm about to create one for myself, hope none thinks this is a unnecessary post.

I don't understand for example what delay is used for, I would love to learn everything about Timers and how they are used.

So I'm creating a 15 puzzle game, the game is done but I wanted to add a another function.

A timer that is displayed on a JLabel. Specifics:

  1. Timer counts down from 5 or 10 minutes.
  2. Wanted format for the timer "mm:ss".
  3. As soon as the first button is pressed the timer starts to countdown.
  4. If solved before the timer runs out you win, else you lose.
  5. If you press on the "New Game" button it resets to 5 or 10 minutes.
  6. And the countdown starts again as soon as you press the first button in the game.

I would love if you could explain what the params are used for and what Timer I should use for my case. Also I'm using java swing.

Here is my game so far if you are interested:

class GameLogic extends JFrame implements ActionListener {

private JPanel grid = new JPanel();
private JPanel overHead = new JPanel();
private JButton newGameButton = new JButton("NEW GAME");
private JButton[][] buttons = new JButton[4][4];
private JButton[][] winPattern = new JButton[4][4];
private JButton button0 = new JButton("");
private JLabel timerLabel = new JLabel("TIMER");
private int emptyIndex;
private int sourceIndex;
private int sourceRow;
private int sourceCol;
private int blankRow;
private int blankCol;
private int movesCounter = 0;
private JLabel movesLabel = new JLabel("MOVES");

public GameLogic() {
    setLayout(new BorderLayout());
    add(overHead, BorderLayout.NORTH);
    overHead.setLayout(new BorderLayout());
    overHead.add(newGameButton, BorderLayout.WEST);
    overHead.add(movesLabel, BorderLayout.EAST);
    overHead.add(timerLabel, BorderLayout.CENTER);
    movesLabel.setFont(new Font("Street Cred", Font.PLAIN, 20));
    newGameButton.setPreferredSize(new Dimension(100, 50));
    newGameButton.setFont(new Font("Street Cred", Font.PLAIN, 13));
    overHead.setBorder(new EmptyBorder(50, 50, 10, 50));
    grid.setBorder(new EmptyBorder(50, 50, 50, 50));
    newGameButton.addActionListener(this);
    add(grid, BorderLayout.CENTER);
    setBackground(Color.RED);
    grid.setLayout(new GridLayout(4, 4));
    try {
        GraphicsEnvironment ge =
                GraphicsEnvironment.getLocalGraphicsEnvironment();
        ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, new File("street cred.ttf")));
    } catch (IOException | FontFormatException e) {
        //Handle exception
    }
    int i = 1;
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            if (row == 3 && col == 3) {
                buttons[row][col] = button0;
                grid.add(buttons[row][col]);
                buttons[row][col].setBackground(Color.WHITE);
                buttons[row][col].setName("button0");
                winPattern[row][col] = button0;
            } else {
                buttons[row][col] = new JButton(i + "");
                grid.add(buttons[row][col]);
                buttons[row][col].addActionListener(this);
                buttons[row][col].setBackground(Color.RED);
                buttons[row][col].setName("button" + i);
                buttons[row][col].setFont(new Font("Street Cred", Font.PLAIN, 40));
                winPattern[row][col] = buttons[row][col];
                i++;
            }
        }
    }

    do {
        shuffle();
    } while (!isSolvable());
    try {
        setIconImage(ImageIO.read(new File("C:\\Users\\Allan\\Documents\\Nackademin\\OOP\\test2\\icon.png")));
    } catch (IOException e) {
        e.printStackTrace();
    }
    movesLabel.setText("<html>MOVES<br><html>" + "----- " + movesCounter + " -----");
    setTitle("PUZZLE GAME");
    setCursor(new Cursor(Cursor.HAND_CURSOR));
    setResizable(false);
    setLocation(500, 200);
    setSize(600, 600);
    setVisible(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
}

public boolean isSolvable() {
    int inv_counter = 0;
    int[] values = new int[16];
    // Lägger alla komponenters nummer i en int array
    for (int i = 0; i < grid.getComponents().length; i++) {
        if (grid.getComponents()[i] == button0) {
            values[i] = 0;
        } else {
            JButton temp = (JButton) grid.getComponents()[i];
            values[i] = Integer.parseInt(temp.getText());
        }

    }

    for (int i = 0; i < values.length - 1; i++) {
        for (int j = i + 1; j < values.length; j++) {
            if (values[i] < values[j]) {
                inv_counter++;
            }
        }
    }

    return inv_counter % 2 == 0;
}


public boolean isSwappable(JButton button) {

    // för att hitta platsen på knappen man trycker och även den blanka platsen
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            if (buttons[row][col] == button) {
                sourceRow = row;
                sourceCol = col;
            } else if (buttons[row][col] == button0) {
                blankRow = row;
                blankCol = col;
            }
        }
    }
    sourceIndex = (sourceRow * 4) + sourceCol;
    emptyIndex = (blankRow * 4) + blankCol;
    // om den är till höger
    if (sourceCol != 3 && sourceRow == blankRow && buttons[sourceRow][sourceCol + 1] == button0) {
        return true;
    }
    // om den är till vänster
    else if (sourceCol != 0 && sourceRow == blankRow && buttons[blankRow][sourceCol - 1] == button0) {
        return true;
    }
    //om den är nedanför
    else if (sourceRow != 0 && sourceCol == blankCol && buttons[sourceRow - 1][sourceCol] == button0) {
        return true;
    }
    //om den är ovanför
    else if (sourceRow != 3 && sourceCol == blankCol && buttons[sourceRow + 1][sourceCol] == button0) {
        return true;
    }
    return false;
}

public void swap(JButton source) {
    JButton tempButton = buttons[sourceRow][sourceCol];
    buttons[sourceRow][sourceCol] = buttons[blankRow][blankCol];
    buttons[blankRow][blankCol] = tempButton;
    grid.remove(button0);
    grid.remove(source);

    if (emptyIndex < sourceIndex) {
        grid.add(source, emptyIndex);
        grid.add(button0, sourceIndex);
    } else if (emptyIndex > sourceIndex) {
        grid.add(button0, sourceIndex);
        grid.add(source, emptyIndex);
    }
    revalidate();
    repaint();
}

public void shuffle() {
    Random random = new Random();

    //randomize positions for 2D array buttons
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            int randomNumber = random.nextInt(16);
            int randomRow = randomNumber / 4;
            int randomCol = randomNumber % 4;
            JButton temp = buttons[row][col];
            buttons[row][col] = buttons[randomRow][randomCol];
            buttons[randomRow][randomCol] = temp;
        }
    }
    //remove all components from panel
    grid.removeAll();

    // add components with randomized position to panel
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            grid.add(buttons[row][col]);
        }
    }
    revalidate();
    repaint();
}

public boolean isSolved() {
    int counter = 0;
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            if (winPattern[row][col].getText().equals(buttons[row][col].getText())) {
                counter++;
            }
        }
    }
    if (counter == 16) {
        return true;
    } else {
        return false;
    }
}

public void moves() {
    movesCounter++;
    movesLabel.setText("<html>MOVES<br><html>" + "----- " + movesCounter + " -----");
}

public void timerCountDown() {
}

public void reset() {
    movesCounter = 0;
    movesLabel.setText("<html>MOVES<br><html>" + "----- " + movesCounter + " -----");
}

public void newGame() {
    do {
        shuffle();
        reset();
    } while (!isSolvable());
}

@Override
public void actionPerformed(ActionEvent e) {
    JButton source = (JButton) e.getSource();

    if (source == newGameButton) {
        newGame();
    } else if (isSwappable(source)) {
        swap(source);
        moves();
    }

    if (isSolved()) {
        JOptionPane.showMessageDialog(null, "YOU BEAT THE GAME!\nMoves: " + movesCounter + "\nTime: ");
        newGame();
    }
}
}

You can use a ScheduledExecutorService ( https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html )

They allow you to run a task (in your case, a timer) at a precise moment, and to repeat the task after a given delay.

To create your ScheduledExecutorService:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

Then you need to run your task, including delay and period:

ScheduledFuture<?> handler = scheduler.scheduleAtFixedRate(yourTask, 0, 1, TimeUnit.SECONDS);

"yourTask" is a Runnable. You can either use a lambda, such as () -> System.out.println("BEEP") or a method reference, such as this::run , with a run method that would display on your screen the time. You can have two fields minutes and seconds initially set at 5 and 0 (or 10 and 0 of you want 10 minutes , that you would change every time the run` method is called, then display on your screen the correct time:

private void run(){
    seconds--;
    if(seconds < 0) {
        minutes--;
        seconds = 59;
    }
    displayTimeOnScreen();

    if(minutes == 0 && seconds == 0) {
        handler.cancel(true);
    }
}

You can use handler.cancel(true) to stop the task.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM