3

ボタンの ActionListener のメソッドから「メイン スレッド」(実行されるスレッドmain()) に何らかの処理を実行させactionPerformed()たいのですが、これを実現する方法がわかりません。

もう少しコンテキスト:

現在、Swing (テトリスのフレーバー) を使用して 2D ゲームをプログラミングしています。
アプリケーションが起動すると、ゲームのメイン メニューを表示するウィンドウが開きます。ユーザーにはいくつかの可能性が提示されます。そのうちの 1 つは、「開始」ボタンを押してゲームを開始することです。これにより、ゲーム パネルが表示され、ゲームのメイン ループがトリガーされます。

2 つのパネル (メイン メニューのパネルとゲームのパネル) を切り替えることができるようにするために、CardLayout マネージャーを使用していますshow()
アイデアは、スタート ボタンに次のようなリスナーを持たせたいということです。

public class StartListener implements ActionListener {
    StartListener() {}
    public void actionPerformed(ActionEvent e) {
        displayGamePanel();
        startGame();
    }
}

actionPerformed()がイベント ディスパッチ スレッドから呼び出されるため、これは機能しません。そのため、 startGame()(メイン ループをトリガーする: ゲーム ロジックの更新 +repaint()各フレームでの呼び出し) への呼び出しは、スレッド全体をブロックします。

私が現在これを処理している方法はactionPerformed()、ブール値のフラグ値を変更することです。public void actionPerformed(ActionEvent e) { startPushed = true; }
これは、最終的にメインスレッドによってチェックされます。

while (true) {
    while (!g.startPushed) {
        try { 
            Thread.sleep(100); 
        } catch (Exception e) {}
    }
    g.startPushed = false;
    g.startGame();
}

しかし、この解決策は非常に洗練されていないと思います。

Concurrency in Swingのレッスンを読みましたが、まだ混乱しています (ワーカースレッドを実装する必要がありますか? それは少しやり過ぎではありませんか?)。実際のマルチスレッド作業はまだ行っていないので、少し迷っています。

メインスレッド(無期限にスリープし、ユーザーのアクションを待っている)に「よし、今起きてこれをする(ゲームパネルを表示してゲームを開始する)」ように伝える方法はありませんか?

ご協力いただきありがとうございます。

編集: 明確にするために、これは私のゲームループがどのように見えるかです:

long lastLoopTime = System.currentTimeMillis();
long dTime;
int delay = 10;
while (running) {
    // compute the time that has gone since the last frame
    dTime = System.currentTimeMillis() - lastLoopTime;
lastLoopTime = System.currentTimeMillis();

    // UPDATE STATE
    updateState(dTime);
    //...

    // UPDATE GRAPHICS
    // thread-safe: repaint() will run on the EDT
    frame.repaint()

    // Pause for a bit
    try { 
    Thread.sleep(delay); 
    } catch (Exception e) {}
}
4

5 に答える 5

4

これは意味がありません:

しかし、actionPerformed() がイベント ディスパッチ スレッドから呼び出されるため、これは機能しません。そのため、startGame() の呼び出し (メイン ループをトリガーします: 各フレームでのゲーム ロジックの更新 + repaint() 呼び出し) は、スレッド全体をブロックします。

ゲーム ループが EDT をブロックしてはならないためです。ゲーム ループにスイング タイマーまたはバックグラウンド スレッドを使用していますか? そうでない場合は、そうしてください。

それにかんする:

while (true) {
    while (!g.startPushed) {
        try { 
            Thread.sleep(100); 
        } catch (Exception e) {}
    }
    g.startPushed = false;
    g.startGame();
}

これもしないでください。代わりに、この種のことにはリスナーを使用してください。

例えば、

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class GameState extends JPanel {
   private CardLayout cardlayout = new CardLayout();
   private GamePanel gamePanel = new GamePanel();
   private StartPanel startpanel = new StartPanel(this, gamePanel);

   public GameState() {
      setLayout(cardlayout);
      add(startpanel, StartPanel.DISPLAY_STRING);
      add(gamePanel, GamePanel.DISPLAY_STRING);
   }

   public void showComponent(String displayString) {
      cardlayout.show(this, displayString);
   }

   private static void createAndShowGui() {
      GameState mainPanel = new GameState();

      JFrame frame = new JFrame("GameState");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class StartPanel extends JPanel {
   public static final String DISPLAY_STRING = "Start Panel";

   public StartPanel(final GameState gameState, final GamePanel gamePanel) {
      add(new JButton(new AbstractAction("Start") {

         @Override
         public void actionPerformed(ActionEvent e) {
            gameState.showComponent(GamePanel.DISPLAY_STRING);
            gamePanel.startAnimation();
         }
      }));
   }
}

class GamePanel extends JPanel {
   public static final String DISPLAY_STRING = "Game Panel";
   private static final int PREF_W = 500;
   private static final int PREF_H = 400;
   private static final int RECT_WIDTH = 10;
   private int x;
   private int y;

   public void startAnimation() {
      x = 0;
      y = 0;
      int timerDelay = 10;
      new Timer(timerDelay , new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent e) {
            x++;
            y++;
            repaint();
         }
      }).start();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.fillRect(x, y, RECT_WIDTH, RECT_WIDTH);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }
}
于 2012-04-21T22:10:32.663 に答える
1

SwingWorkerを使用する必要があります。これdoInBackground()により、バックグラウンド スレッドでコードが実行され、停止done()後に EDT でコードが実行されます。doInBackground()

于 2012-04-21T22:15:25.310 に答える
0

最も簡単な方法: を使用しCountDownLatchます。これを 1 に設定し、適切な方法で Swing コードで使用できるようにし、メイン スレッドで使用できるようにしますawait

于 2012-04-21T22:06:34.940 に答える
0

SwingUtilities.invokeAndWait() を使用してゲーム パネルでモーダル ダイアログを表示し、ダイアログが閉じられたときにコントロールがメイン スレッドに戻るようにすることを検討できます。

于 2012-04-21T22:15:03.953 に答える
0

EDT を除くすべてのコードをシングル スレッド実行サービスで実行し、コードを実行する必要があるときはいつでもランナブルをポストすることができます。

于 2012-04-21T22:20:40.507 に答える