2012-04-21 5 views
3

Я хочу вызвать «основной поток» (начальный поток, который запускает main()), чтобы выполнить некоторую работу с помощью метода ActionListener кнопки, но я не знаю, как этого добиться.Java Swing: Как просыпаться основной поток из потока событий?

Побольше контекст:

Я в настоящее время программирования 2D игры с использованием свинга (аромат тетриса).
Когда приложение запускается, открывается окно с отображением главного меню игры. Пользователю предлагается несколько возможностей, один из которых - начать игру, нажав кнопку «Пуск», которая вызывает отображение игровой панели и запускает основной цикл игры.

Чтобы иметь возможность переключаться между двумя панелями (в главном меню и в игре), я использую диспетчер 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, но я все еще смущен (должен ли я реализовать Рабочую нить - не так ли немного перебор?). Я еще не выполнял многопоточную работу, поэтому я немного потерялся.

Нет ли способа рассказать основную тему (которая будет спать бесконечно, ожидая действия пользователя) «ОК, проснись сейчас и сделай это (покажите панель игры и начните игру)» ?.

Благодарим за помощь.

EDIT: Просто чтобы быть ясно, это то, что моя игра цикл выглядит следующим образом:

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

ответ

1

вы должны использовать SwingWorker это будет выполнять код в doInBackground() в фоновом потоке и код в done() в EDT после doInBackground() остановки

0

Самый простой способ: использовать CountDownLatch. Вы установите его на 1, сделайте его доступным в коде Swing любым подходящим способом, а в основном потоке вы найдете await.

+0

Спасибо, я смотрю на эту возможность прямо сейчас, это похоже на то, что я ожидал :) – Rez

4

Это не имеет смысла:

, но это не работает, потому что actionPerformed() вызывается из событий отправки потока, поэтому вызов StartGame() (который запускает основной цикл: игра логическое обновление + repaint() в каждом кадре) блокирует весь поток.

Поскольку ваш игровой цикл не должен блокировать EDT.Используете ли вы таймер Swing или фоновый поток для игрового цикла? Если нет, сделайте это.

Относительно:

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); 
    } 
} 
+0

'startGame()' вызывает триггеры игрового цикла, поэтому цикл игры выполняется в потоке, который вызывает ' StartGame() '. Поэтому, если я вызываю 'startGame()' из 'actionPerformed()', он блокирует EDT, но когда я вызываю его из 'main()' (где я помещаю 'while (true) ...') , он не блокирует EDT и отлично работает. Кроме того, не используется таймер качания, используемый в EDT (что не то, что я хочу здесь сделать)? Я хотел бы использовать такие вещи, как слушатели, но снова единственные слушатели, которых я знаю, работают на EDT, и я хочу, чтобы слушатели работали по моей основной теме. – Rez

+0

@Rez: Опять же, игровой цикл *** не должен блокировать EDT ***. Период. Таймеры качания работают на EDT, но часть задержки таймера, скрытая 'Thread.sleep (...)' не делает, и это ключ. Пожалуйста, см. Изменение, чтобы ответить выше. –

+0

Благодарим вас за ответ (хотя я сказал, что игровой цикл не блокирует EDT). Я прочитал и запустил ваш код, кажется, что вы делаете всю работу над EDT (тщательно используя таймеры, чтобы не блокировать весь поток)? (Я только что проверил с помощью 'SwingUtilities.isEventDispatchThread()', работа в 'startAnimation()' и в соответствующем 'actionPerformed' выполняется на EDT). Я думал, что мы не должны делать всю тяжелую работу (логика игры, обновления состояния и все такое) в EDT, но в отдельном потоке, не так ли? Извините, я немного смущен. Еще раз спасибо. – Rez

0

Вы можете рассмотреть показывает модальный диалог с игровой панелью с помощью SwingUtilities.invokeAndWait(), так что, когда диалог закрывается управление возвращается обратно в основной поток.

+0

Спасибо, это должно сработать, однако я бы предпочел придерживаться только панели меню если возможно (я хотел бы оставить только одно окно, также, я не уверен, что диалоги хорошо встроены в апплеты)? – Rez

+0

Хмм, мой ответ был основан на идее, что вы хотите, чтобы первая панель была закрыта (чтобы пользователь сделал выбор), а затем запустил другой дисплей.Вероятно, правильным способом было бы прекратить делать что-либо вообще в основном потоке и делать все асинхронно с помощью таймеров качания, как уже было предложено другими людьми. Обычно в swing-приложениях основной поток имеет ограниченное задание, просто чтобы установить рамку или диалог на видимые. –

0

Вы можете сделать весь код, за исключением запуска EDT на одном потоке службы исполнения а затем просто отправлять runnables всякий раз, когда вам нужен какой-то код.

Смежные вопросы