简体   繁体   中英

How could I add a refreshing timer to a JLabel using swing components

I am creating a Maze game and want a timer to be displayed on the gameStage. I have tried using java.util but it requires me to get rid of my swing timer.How could i add a refreshing timer to game

This code is used to make the game frame which contains the button pane and the gameStage.

import java.awt.CardLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

/**
 * This class Holds the game pane that has the moving player. It also contains
 * the GamePane
 * 
 * @author 602052004
 *
 */

public class GamePane extends JPanel implements ActionListener, KeyListener {// *change
                                                                                // GamePane
                                                                                // to
                                                                                // GamePane
    // This is were the game screen is made and the player is created.

    static final long serialVersionUID = 1L;
    JLabel player = new JLabel();
    JLabel finish = new JLabel();
    JFrame gameFrame;
    int playerSpeed = 4;
    int FPS = 40;
    // This array holds my JLabels for the walls.I used it so that i can have a
    // for loop with an index for the labels.
    JLabel[] walls = new JLabel[3];
    {

        walls[0] = new JLabel();
        walls[1] = new JLabel();
        walls[2] = new JLabel();

    }
    private final Set<Integer> keys = new HashSet<>();

    // The keys set holds the keys being pressed

    public static void main(String[] args) {
        // Open the GUI window
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // Create a new object and
                // run its go() method
                new GamePane().go();
            }
        });
    }

    GamePane() {
        // Run the parent class constructor
        super();
        // Allow the panel to get focus
        setFocusable(true);
        // Don't let keys change the focus

    }

    /**
     * This method creates the gameFrame and sets its layout to a cardlayout.It
     * then proceeds the set up the GameFrame.The gameFrame contains the button
     * pane and the gameStage
     * 
     * The walls are an array and are used to create an index which is then used
     * for the collisions.I set up the walls location here
     */
    protected void go() {
        setLayout(new CardLayout());
        // Setup the window
        gameFrame = new JFrame();
        // Add this panel to the window
        gameFrame.setLayout(new CardLayout());
        gameFrame.add(this, "main");
        gameFrame.setContentPane(this);

        // Set's the window properties
        gameFrame.setTitle("main");
        gameFrame.setSize(800, 600);
        gameFrame.setResizable(false);
        gameFrame.setLocationRelativeTo(null);
        gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gameFrame.setVisible(true);
        gameFrame.add(new ButtonPane(gameFrame), "buttons");
        // Creates the new JPanel that will hold the game.
        JPanel gamestage = new JPanel();
        gamestage.setBackground(Color.darkGray);
        gameFrame.add(gamestage, "game");
        gamestage.setLayout(null);
        // *Move the setup of the player and the timer under the walls
        // Get a sample of collisions going so that i can do it over the weekend
        // Setup the movable box
        player.setBounds(25, 25, 20, 20);
        player.setVisible(true);
        player.setBackground(Color.red);
        // Opaque makes the background visible
        player.setOpaque(true);

        // Setup the key listener
        addKeyListener(this);
        // Null layout allows moving objects!!!
        gamestage.add(player);
        // Set the timer
        Timer tm = new Timer(1000 / FPS, this);
        tm.start();

        walls[0].setBounds(10, 15, 10, 480);// left height
        walls[0].setVisible(true);
        walls[0].setBackground(Color.white);
        walls[0].setOpaque(true);
        gamestage.add(walls[0]);

        walls[1].setBounds(10, 10, 490, 10);// top width
        walls[1].setVisible(true);
        walls[1].setBackground(Color.white);
        walls[1].setOpaque(true);
        gamestage.add(walls[1]);

        // wall3.setBounds(x, y, width, height);
        walls[2].setBounds(10, 100, 100, 10);
        walls[2].setVisible(true);
        walls[2].setBackground(Color.white);
        walls[2].setOpaque(true);
        gamestage.add(walls[2]);



        finish.setBounds(30, 455, 20, 20); // *make the game change to the main
                                            // screen when finished
                                            // Add a timer
        finish.setVisible(true);
        finish.setBackground(Color.LIGHT_GRAY);
        finish.setOpaque(true);
        gamestage.add(finish);

    }

    /**
     * Check if two JLabel objects are touching
     * 
     * @param a
     *            The first JLabel
     * @param b
     *            The second JLabel
     * @return true if the JLabels are touching
     */
    public boolean areColliding(JLabel a, JLabel b) {
        return a.getBounds().intersects(b.getBounds());
    }

    /**
     * this method makes the player move. It takes the players speed and
     * subtracts or adds the player speed to the current position of the player.
     * It also figures out were the player is at currently aswell.
     * 
     * @param arg0
     */
    @Override
    public void actionPerformed(ActionEvent arg0) {
        // Move up if W is pressed
        if (keys.contains(KeyEvent.VK_W)) {
            player.setLocation(player.getX(), player.getY() - playerSpeed);
        }
        // Move right if D is pressed
        if (keys.contains(KeyEvent.VK_D)) {
            player.setLocation(player.getX() + playerSpeed, player.getY());
        }
        // Move down if S is pressed
        if (keys.contains(KeyEvent.VK_S)) {
            player.setLocation(player.getX(), player.getY() + playerSpeed);
        }
        // Move left if A is pressed
        if (keys.contains(KeyEvent.VK_A)) {
            player.setLocation(player.getX() - playerSpeed, player.getY());
        }

        for (int i = 0; i < walls.length; i++) {
            // I created a for loop instead
            // of a do loop because the for
            // loop would have been a lot
            // simpler to manage

            if (areColliding(walls[i], player)) { // Reposition the target
                int newX = (int) (25);
                int newY = (int) (25);
                player.setLocation(newX, newY);

            }
        }

        if (areColliding(finish, player)) {
            // Reposition the target
            int newX = 25;
            int newY = 25;
            player.setLocation(newX, newY);
            CardLayout layout = (CardLayout) gameFrame.getContentPane()
                    .getLayout();
            layout.show(gameFrame.getContentPane(), "buttons");
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // Add the key to the list
        // of pressed keys
        if (!keys.contains(e.getKeyCode())) {
            keys.add(e.getKeyCode());
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // Remove the key from the
        // list of pressed keys
        keys.remove((Integer) e.getKeyCode());
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

}

this code shows the game pane when the buttob is pressed

/**
 * This pane contains the button and sets up the button pane
 */
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class ButtonPane extends JPanel {

    private JButton startBTN;// Calls the JButton
    JFrame game;

    public ButtonPane(JFrame g) {
        game = g;
        setLayout(new GridBagLayout());
        setBackground(Color.gray);// Sets the menu stages color blue
        startBTN = new JButton("Game");// Creates a new button
        add(startBTN);// Adds the button on the startStage

        startBTN.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (game.getContentPane().getLayout() instanceof CardLayout) {
                    CardLayout layout = (CardLayout) getParent().getLayout();
                    layout.show(game.getContentPane(), "game");

                }
            }
        });
    }

}

Okay, so you have a Timer already, which is ticking away at 1000/FPS times a second, cool. All you really need is away to calculate the difference between two points in time, which, amazingly, is very simple.

Start by defining a "start time"

private Instant startTime;

This will be null till you need it. When you want to start the timer, use startTime = Instant.now();

When startTime != null , you want to calculate the difference between it and now...

Duration runningTime = Duration.between(startTime, Instant.now());

This now tells you how long the timer has been running for.

Next, we need to make some decisions, like what to do when the timer runs out, but for that, we actually need to know how lone the timer should run for...

private Duration timeOutDuration = Duration.ofSeconds(5);

This just sets up a timeout of 5 seconds, you can use what ever range you want.

This then allows us to calculate the remaining time of the timer...

Duration timeRemainig = timeOutDuration.minus(runningTime);

and then to make decisions about what to do...

if (timeRemainig.isNegative() || timeRemainig.isZero()) {
    // Time has run out...
    // startTime = null; // stop the timer
} else {
    // Update the UI
}

The date/time API introduced in Java 8 is incredibly powerful and flexible (and a lot of fun, when you get your head around it)

A solution might start looking something like...

private Duration timeOutDuration = Duration.ofSeconds(5);
private Instant startTime; // Set this when you're ready to start the timer

@Override
public void actionPerformed(ActionEvent arg0) {
    if (startTime != null) {
        Duration runningTime = Duration.between(startTime, Instant.now());
        Duration timeRemainig = timeOutDuration.minus(runningTime);
        if (timeRemainig.isNegative() || timeRemainig.isZero()) {
            // Time has run out...
            // startTime = null; // stop the timer
        } else {
            // Update the UI
        }
    }

Formatting a Duration for output generally looks something like...

long hours = timeRemainig.toHours();
long mins = timeRemainig.minusHours(hours).toMinutes();

// Or if you're lucky enough to be using Java 9+
//String formatted = String.format("%dhrs %02dmins", duration.toHours(), duration.toMinutesPart());
String formatted = String.format("%dhrs %02dmins", hours, mins);

or simular, depending on how you want it formatted

Why use this approach instead of some "counter"

Simple, it's (super) accurate. Timer only guarantees a "at least" interval, that is, it will delay no less then the value apply, this means that it's possible to introduce a "drag" over time, where a counter would fall out of sync. Sure, over a short period of time, it's probably not a big deal, but since there is a (super easy) better way to do it, why not make use of it.

The solution is also super flexible, applied to a broad spectrum of similar issues. I use the above concept as part of time based animations, which generally produce a far superior, overall, result.

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