2

私は別の壁にぶつかった。キー入力が機能した後、私は何時間も頭を悩ませてきました。一時停止機能を作成したいので、同じキーをもう一度押すと、timertaskの実行が停止します(つまり、ゲームが一時停止されます)。

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



        }}));


        }

問題は、ネストされたクラスのtimerTaskメソッドのためにグローバルブールisRunning varを使用して、キーが押されるたびに切り替えることができないことです(したがって、ブールisRunningはアクセスするためにfinalとして宣言する必要があります...)。キーがもう一度押されたかどうか、またはゲームがすでに実行されているかどうかを検出して、timerTaskを一時停止/キャンセルできるようにする方法に関するアイデア。

サムに感謝します

4

5 に答える 5

6

これはSwingゲームであるため、java.util.Timerではなくjavax.swing.TimerまたはSwingTimerを使用する必要があります。Swingタイマーを使用することで、断続的に呼び出されるコードがEDTで呼び出されることが保証されます。これは、Swingアプリの重要な問題であり、タイマーを一時停止するstopメソッドもあります。匿名のAbstractActionクラスにプライベートブールフィールドを指定して、キーが初めて押されたかどうかを確認することもできます。

また、KeyListenerの代わりにKeyBindingsを使用するための称賛と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;
     }
  }));

もう1つの便利なオプションは、キーを押したときに繰り返しタスクを実行し、キーを離したときに停止することです。これは、押したときと離したときにキーストロークを取得することで簡単に実行できます。

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();
   }
}
于 2011-10-22T12:54:50.393 に答える
0

いいえ、匿名クラスのファイナルが必要な理由について間違った考えを持っていません!Finalは、ローカル変数(より正確には、指定されたオブジェクトよりも有効期間が短い可能性のあるすべての変数)にのみ必要です。

したがって、クラス内の静的変数は完全に問題なく、完全に機能します。

編集:例:

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();
    }
}
于 2011-10-22T12:59:12.727 に答える
0

最も簡単で汚い解決策:

final boolean[] isRunning = new boolean[1];

あなたはそれをしたくありません—しかしそれは周りの適切な同期を前提として機能します。

何が良いでしょう

final AtomicBoolean isRunning = new AtomicBoolean();

さらに良いのは、設計をもう一度確認することです。グローバル状態は通常、「グローバルな問題」を意味します。

于 2011-10-22T12:54:47.220 に答える
0

最終的な修飾子の要件は簡単に回避できます。内部メソッド(final要件があります)をクラスメソッドの呼び出しに置き換えます。

于 2011-10-22T12:57:28.983 に答える
0

ゲームクラスなど、どこかにタイマーへの参照を保持します。ゲームが一時停止したら、タイマーをキャンセルします。これにより、現在スケジュールされているタスクがキャンセルされます。次に、ゲームが一時停止されていないときに、上記のようにタイマーを再度スケジュールします。

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

    }
}
于 2011-10-22T13:04:29.720 に答える