我撞到了另一堵墙。让我的键输入工作后,我已经绞尽脑汁几个小时,我想创建一个暂停功能,这样如果再次按下相同的键,时间任务停止运行(即游戏暂停)
JPanel component = (JPanel)frame.getContentPane();
component.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "space");
component.getActionMap().put("space", (new AbstractAction(){
public void actionPerformed(ActionEvent e){
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
public void run(){
grid.stepGame();
}
},250, 250);
}}));
}
问题是我无法使用全局布尔值isRunning var并在每次按下键时切换它,因为嵌套类中的timerTask方法(因此布尔值isRunning必须被声明为final才能被访问...)。有关如何检测是否再次按下该键或游戏是否已经运行的任何想法,我可以暂停/取消我的timerTask。
非常感谢Sam
答案 0 :(得分:6)
由于这是一个Swing游戏,你应该使用javax.swing.Timer或Swing Timer而不是java.util.Timer。通过使用Swing Timer,您可以保证在EDT上调用间歇性调用的代码,这是Swing应用程序的一个关键问题,它还有一个暂停Timer的stop方法。您还可以为匿名的AbstractAction类提供一个私有布尔字段,以检查是否第一次按下了该键。
另外,使用Key Bindings而不是KeyListener的kudos和1+。
如,
JPanel component = (JPanel) frame.getContentPane();
component.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "space");
component.getActionMap().put("space", (new AbstractAction() {
private boolean firstPress = true;
private int timerDelay = 250;
private javax.swing.Timer keyTimer = new javax.swing.Timer(timerDelay , new ActionListener() {
// Swing Timer's actionPerformed
public void actionPerformed(ActionEvent e) {
grid.stepGame();
}
});
// key binding AbstractAction's actionPerformed
public void actionPerformed(ActionEvent e) {
if (firstPress) {
keyTimer.start();
} else {
keyTimer.stop();
}
firstPress = !firstPress;
}
}));
另一个有用的选择是在按键上执行重复任务并在按键释放时停止它,这可以通过按下按键和释放时轻松完成:
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true) // for key release
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false) // for key press
例如:
import java.awt.event.*;
import javax.swing.*;
public class SwingTimerEg2 {
private JFrame frame;
private Grid2 grid = new Grid2(this);
private JTextArea textarea = new JTextArea(20, 20);
private int stepCount = 0;
public SwingTimerEg2() {
frame = new JFrame();
textarea.setEditable(false);
frame.add(new JScrollPane(textarea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
setUpKeyBinding();
}
void setUpKeyBinding() {
final int timerDelay = 250;
final Timer keyTimer = new Timer(timerDelay, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
grid.stepGame();
}
});
JPanel component = (JPanel) frame.getContentPane();
final int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
final String spaceDown = "space down";
final String spaceUp = "space up";
component.getInputMap(condition).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), spaceDown);
component.getActionMap().put(spaceDown, (new AbstractAction() {
public void actionPerformed(ActionEvent e) {
keyTimer.start();
}
}));
component.getInputMap(condition).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), spaceUp);
component.getActionMap().put(spaceUp, (new AbstractAction() {
public void actionPerformed(ActionEvent e) {
keyTimer.stop();
}
}));
}
public void doSomething() {
textarea.append(String.format("Zap %d!!!%n", stepCount));
stepCount ++;
}
private static void createAndShowGui() {
new SwingTimerEg2();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Grid2 {
private SwingTimerEg2 stEg;
public Grid2(SwingTimerEg2 stEg) {
this.stEg = stEg;
}
void stepGame() {
stEg.doSomething();
}
}
答案 1 :(得分:0)
最简单,最脏的解决方案:
final boolean[] isRunning = new boolean[1];
您不希望这样做 - 但假设正确同步,它可以正常工作。
更好的是
final AtomicBoolean isRunning = new AtomicBoolean();
更好的是再次审查设计:全球状态通常意味着“全球问题”
答案 2 :(得分:0)
可以轻松避免最终限定符要求 - 使用类方法调用替换内部方法(具有final
要求)。
答案 3 :(得分:0)
对于你为什么需要匿名课程的最终决定,你没有错误的想法!只有局部变量才需要最终(更确切地说,任何可能有比实际时间短的变量的变量)。
因此,类中的静态变量非常精细,并且可以完美运行!
编辑:示例:
public class Main {
interface Test {
void call();
}
public static volatile boolean running = true;
public static void main(String[] args) {
Test t = new Test() {
@Override
public void call() {
System.out.println(Main.running);
}
};
t.call();
running = false;
t.call();
}
}
答案 4 :(得分:0)
在某个地方保留对Timer的引用,比如你的游戏类。 当游戏暂停时取消计时器。 这将取消任何当前计划的任务。 然后当游戏未暂停时,再次安排计时器,如上所述。
public class Game {
private Timer timer;
public void pause() {
if (timer != null) {
timer.pause();
}
}
public void startOrResumeGame() {
if (timer == null) {
timer = new Timer();
} else {
// Just in case the game was already running.
timer.cancel();
}
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
grid.stepGame();
}
}, 250, 250);
}
}