如何使用swing组件为JLabel添加刷新计时器

我正在创建一个迷宫游戏,并希望在gameStage上显示一个计时器。 我已经尝试过使用java.util但它需要我摆脱我的摇摆计时器。我怎么能为游戏添加一个刷新的计时器

此代码用于制作包含按钮窗格和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 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) { } } 

此代码显示按下buttob时的游戏窗格

 /** * 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"); } } }); } } 

好的,所以你已经有了一个Timer ,每秒1000/FPS次,很酷。 你真正需要的是计算两个时间点之间的差异,这非常简单。

首先定义“开始时间”

 private Instant startTime; 

在您需要之前,这将为null 。 如果要启动计时器,请使用startTime = Instant.now();

startTime != null ,你想要计算它和现在之间的差异……

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

现在告诉您计时器运行了多长时间。

接下来,我们需要做出一些决定,比如当计时器用完时要做什么,但为此,我们实际上需要知道计时器应该如何运行……

 private Duration timeOutDuration = Duration.ofSeconds(5); 

这只是设置5秒的超时,你可以使用你想要的范围。

这样我们就可以计算出计时器的剩余时间……

 Duration timeRemainig = timeOutDuration.minus(runningTime); 

然后决定做什么……

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

Java 8中引入的日期/时间API非常强大且灵活(当您了解它时,它会带来很多乐趣)

一个解决方案可能会开始看起来像……

 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 } } 

格式化输出的Duration通常看起来像……

 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); 

或类似的,取决于您希望如何格式化

为什么使用这种方法而不是一些“反击”

简单,它(超级)准确。 Timer仅保证“至少”间隔,也就是说,它将延迟不小于应用的值,这意味着随着时间推移可能引入“拖动”,其中计数器将失去同步。 当然,在很短的时间内,这可能不是什么大问题,但是因为有一种(超级简单)更好的方法,为什么不利用它。

该解决方案也非常灵活,适用于广泛的类似问题。 我将上述概念用作基于时间的动画的一部分,这通常会产生更优越的整体效果。