1

I'm trying to code a simple game in Java. The basic structure is a single JFrame with different JPanels that I add/remove at different times. At startup, there is a JPanel that's a basic menu (start game, high scores, etc). Once the "Start" button is pressed it switches to a level selector panel with three buttons to select the difficult level of the game. Once any of the three buttons is pressed, it switches to another panel that will displays a three second countdown, then the actual game. All three buttons call the same method, just with a different difficulty value passed in.

I have all the separate pieces working fine, but I'm having troubles with the transition from the level selection panel to the countdown. If I don't use threads the screen freezes on button press and does not switch to the new panel. I've tried messing around with threads, but I don't know that much about them and have only had limited success (I've got it so it will successfully switch some of the time, but not consistently).

In terms of code, in the level selection panel I have something like this listening for button clicks:

private class ButtonClickedListener implements ActionListener {
    public void actionPerformed(ActionEvent evt) {  
        gui.newLevel(1);
    }
}

where in place of just gui.newLevel(1) I've messed around with starting new threads and calling the method from them.

The newLevel() method look like:

getContentPane().removeAll();

levelPanel = new LevelPanel(levelNum, this);
add(levelPanel);
validate();

levelPanel.start();

I use very similar code when switching from the start menu JPanel to the level selector panel (again, with an ActionListener on the buttons), which works just fine.

LevelPanel's start() method initializes values for the new JPanel and displays the countdown on screen (currently with the following code, although I messed with putting something like this in the newLevel() method instead) before displaying the actual game:

try {
    Thread.sleep(1000);
    //update countdown number
    validate();
    repaint();
    Thread.sleep(1000);
    //update countdown number
    validate();
    repaint();
    Thread.sleep(1000);
    //update countdown number
    validate();
    repaint();
} catch (Exception e) {
    System.out.println(e);
}

//start game

I would really appreciate any help getting this code to work, and I'm pretty sure some sort of threading is the way to go but I'm not quite sure where/how. Any suggestions and/or code samples would be great!

Thanks in advance!

EDIT: I ended up rewriting the countdown using a timer instead of Thread.sleep(), which fixed part of the problem and the rest of it I eventually figured out and was entirely unrelated to GUI stuff, which is why I didn't think to check it in the first place.

Lahiru Ashan
  • 767
  • 9
  • 16
scaevity
  • 3,991
  • 13
  • 39
  • 54

3 Answers3

7

never really never use Thread.sleep(1000); during EDT, this code caused freeze on GUI is un_resposible, untill a new event invoke EDT or mouse hover over can alive this container too

1) there are two ways how to dealy any event(s) in the Swing GUI, by implements

  • Swing Timer

  • delaying by using Thread.sleep(1000); in the SwingWorker

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Can you give me an example with one (or both) of these (with code)? I haven't used Swing Timer and my efforts to get SwingWorker to work haven't been very successful so far. – scaevity Feb 29 '12 at 08:31
  • 1
    @scae [Swing Timer](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) ---> [my posts here](http://stackoverflow.com/search?tab=newest&q=user%3a714968%20timer) and [SwingWorker](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/simple.html) ---> [my posts here](http://stackoverflow.com/search?tab=newest&q=user%3a714968%20swingworker) – mKorbel Feb 29 '12 at 08:42
6

The layout and painting must be done in EDT. Use SwingUtilities.invokeAndWait to call the validate() and repaint()

StanislavL
  • 56,971
  • 9
  • 68
  • 98
0

You can start some code with a time delay using TimerTask:

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    public void run() {
        invokeLater(); // This starts after [delay] ms 
        // and - if given - will run every [period] ms.
    }
}, delay, period);

You could solve your problem with this, though it won't be a pretty solution.

// edit: (see comments) you should synchronize accesses to the gui properly, else it will give you errors.

sinned
  • 503
  • 1
  • 7
  • 25
  • 1
    please edit your post and wrap `doSomething();` into `invokeLater()`, otherwise you can awaiting a few down_votes to your really wrong answer that causing the same (as OP's) issue.. – mKorbel Feb 29 '12 at 08:11
  • 1
    *"it won't be a pretty solution."* It won't be a solution at all unless GUI updates are done on the EDT. That is where the [`javax.swing.Timer`](http://docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html) and `SwingWorker` come in handy. – Andrew Thompson Feb 29 '12 at 08:13
  • @mKorbel Beat me! To the right answer, and right comment. :) – Andrew Thompson Feb 29 '12 at 08:14
  • what precisely would this be used for in my code? the countdown? – scaevity Feb 29 '12 at 09:01
  • yes, i would use it for the countdown, and maybe the delayed start of the game. but be careful, this runs in another thread than you GUI, if you update a label or something, you have to use something like [invokeLater()](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#invokeLater%28java.lang.Runnable%29) like written above. – sinned Feb 29 '12 at 09:42