如何使用swing组件为JLabel添加刷新计时器 [英] How could I add a refreshing timer to a JLabel using swing components
问题描述
我正在创建一个迷宫游戏,并希望在gameStage上显示一个计时器。
我尝试过使用java.util但它需要我摆脱我的摇摆计时器。我怎么能为游戏添加一个刷新的计时器
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
这段代码是用于制作包含按钮窗格和gameStage的游戏框架。
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) {
}
}
此代码显示按下buttob时的游戏窗格
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");
}
}
});
}
}
推荐答案
好的,所以你已经有了一个计时器
,这就是每秒 1000 / FPS
的时间,凉。所有你真正需要的只是计算两个时间点之间的差异,这非常简单。
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.
首先定义一个开始时间
private Instant startTime;
这将是 null
直到你需要它为止。当你想启动计时器时,使用 startTime = Instant.now();
This will be null
till you need it. When you want to start the timer, use startTime = Instant.now();
当 startTime!= null
,你想要计算它和现在之间的差异......
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);
这只是设置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
}
Java 8中引入的日期/时间API非常强大且灵活(当你了解它时很有趣)
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解决方案可能会开始看起来像......
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);
或simular,具体取决于您希望如何格式化
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.
这篇关于如何使用swing组件为JLabel添加刷新计时器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!