-1

Hi I'm building a game which includes 3 JPanels on a JFrame, a Startscreen, a DrawingPanel and a GameOver screen. If I just create the DrawingPanel and tell the GameController class to begin updating it works fine, but if I create a StartScreen with a button to start the game, then when I press the start game button the game window does not display, although the game code runs.

EDIT:

I have created a new program which mimics the creation of the JPanels, but excludes all of the game code to make it a bit simpler to follow. Below I have included all the relevant classes:

This class creates a JFrame and two JPanels. It also runs the code that updates the game state and tells the DrawingPanel to repaint.

    public class TestController{

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
private final int FRAME_WIDTH = (int)screenSize.getWidth();
private final int FRAME_HEIGHT = (int)screenSize.getHeight();
public boolean gameStarted = false;
private JFrame gameWindow;
private TestDrawingPanel myDrawingPanel;
private TestStartGame startGame;
int counter;
Set<Rectangle> rects;

//creates a JFrame and all the JPanels
public TestController(String title)
{

    gameWindow = new JFrame(title);
    gameWindow.setSize(FRAME_WIDTH, FRAME_HEIGHT);
    gameWindow.setVisible(true);
    gameWindow.setResizable(false);

    gameWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    startGame = new TestStartGame(getAvailableWidth(), getAvailableHeight());
    startGame.addAL(new StartButton());
    myDrawingPanel = new TestDrawingPanel(getAvailableWidth(), getAvailableHeight());
    gameWindow.add(startGame);
    gameWindow.add(myDrawingPanel);
    myDrawingPanel.setVisible(false);
    myDrawingPanel.setEnabled(false);
    rects = new HashSet();
}

private int getAvailableWidth()
{
    return gameWindow.getWidth() - gameWindow.getInsets().left - gameWindow.getInsets().right;
}

private int getAvailableHeight()
{
    return gameWindow.getHeight() - gameWindow.getInsets().top - gameWindow.getInsets().bottom;
}

//starts the game running
public void startTheGame()
{   

    myDrawingPanel.setEnabled(true);
    startGame.setVisible(false);
    startGame.setEnabled(false);
    myDrawingPanel.setVisible(true);
    gameStarted = true;
    update();
}

public boolean getGameStarted()
{
    return gameStarted;
}

//loop that runs the game code
public void update()
{
    counter = 0;
    while(gameStarted)
    {
        updatePictureState();

        myDrawingPanel.draw(rects);
        myDrawingPanel.repaint();                
    }
}

//updates the game state
public void updatePictureState()
{
    rects.clear();
    for (int i = counter + 10; i < counter + 100; i = i + 10)
    {
        rects.add(new Rectangle(i,i,10,10));
    }
}

//an action listener to be added to the start screen
private class StartButton implements ActionListener
{
    public void actionPerformed(ActionEvent e)
    {
        Object buttonPressed = e.getSource();

        if(buttonPressed.equals(TestStartGame.start))
        {
            startTheGame();
        }
    }
}
}

This class is an extended JPanel with a single button to start the game:

    public class TestStartGame extends JPanel{

private final JPanel buttons;
public static JButton start;

//creates a JPanel with a single button to start the game    
public TestStartGame(int width, int height)
{
    setSize(width, height);
    setLayout(new GridLayout(2,1));
    setBackground(Color.GREEN);

    buttons = new JPanel();
    buttons.setSize(width, height / 2);
    buttons.setBackground(Color.red);
    start = new JButton("Start");
    buttons.add(start);
    add(buttons, BorderLayout.SOUTH);
}            

//adds an action listener to the button    
public void addAL(ActionListener al)
{
    start.addActionListener(al);
}   
}

This class is an extended JPanel and acts as the main screen for the game, being updated to with each cycle of the game to display it's current state:

    public class TestDrawingPanel extends JPanel{
Set<Rectangle> drawSet;

//creates the drawing panel and sets the size and background
public TestDrawingPanel(int width, int height)
{                
    setSize(width, height);
    this.setBackground(Color.CYAN);

    drawSet = new HashSet();        
}


public void draw(Set<Rectangle> platforms)
{
    drawSet.clear();
    drawSet = platforms;
}

//draws the game window
@Override
public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    System.out.println("works here");
    g.setColor(Color.red);
    for (Rectangle r : drawSet)
    {
        g.fillRect((int)r.getX(), (int)r.getY(), (int)r.getWidth(), (int)r.getHeight());
    }
}    
}

If I just add the TestDrawingPanel, it displays fine, but if I start with a TestStartScreen then when I click the start game button the TestStartScreen does not disappear and the TestDrawingPanel never displays. Interestingly, if I have both screens but do not call the update method is TestController then the start game button works correctly and the TestDrawingPanel displays, although obviously nothing happens as the update method is where the game state is changed.

I have discovered the problem is that if the TestDrawingPanel is not the only JPanel created then the call to repaint it fails.

  • 1) For better help sooner, post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). 2) Use a [`CardLayout`](http://download.oracle.com/javase/8/docs/api/java/awt/CardLayout.html) as shown in [this answer](http://stackoverflow.com/a/5786005/418556). – Andrew Thompson Apr 04 '17 at 11:35
  • I have tried this and it still doesn't help. I'm really stuck on this. If I start the game on myDrawingPanel (the window that displays the actual game) it works fine, but if I start with a title screen that has a button to get the game running then myDrawingPanel.repaint() never gets called, although the rest of the game code still runs fine. I will try to make a Minimal, Complete and Verifiable example as you say and see if that helps. – Nathan Moody Apr 05 '17 at 10:28
  • *"I have tried this.."* I don't see an MCVE. – Andrew Thompson Apr 05 '17 at 10:30
  • Sorry, I meant I'd tried using CardLayout. I'm making an MVCE now. – Nathan Moody Apr 05 '17 at 10:34
  • Let me know when there is an MCVE. – Andrew Thompson Apr 05 '17 at 10:53
  • Hi Andrew, I've created a new program which I think fits the criteria for an MVCE. I hope it's a little easier to follow. Many thanks. – Nathan Moody Apr 05 '17 at 13:22
  • HI Andrew, did you have any solution for this? I've been playing around with invokeLater as suggested below but am still scratching my head. – Nathan Moody Apr 07 '17 at 11:51
  • An MCVE should include imports, a `main` method, and have only a single `public` class (the one with the `main` method). It should be a single copy/past, compile with 0 changes, run type deal. It should also remove irrelevan crap `Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();`. Seriously, get it working at the natural size of the GUI first! – Andrew Thompson Apr 07 '17 at 11:53
  • `while(gameStarted) ..` No, no a thousand times no. An infinite loop will likely freeze the GUI. Use a Swing based `Timer` to call those code statements in the loop, in the `actionPerformed` method of an `ActionListener`. – Andrew Thompson Apr 07 '17 at 11:59
  • OK thank you. I'll have a look into swing timers. Sorry I got the mvce wrong, I'm new to all this. It'll be better next time – Nathan Moody Apr 07 '17 at 12:04
  • 1
    Andrew thank you so much! Using a timer has solved the issue with the added bonus it gives me more control over the timings of the game. I still have a lot to learn I think with Swing components. Again, many thanks. I don't know how to upvote your answer as it's a comment and not a separate answer. – Nathan Moody Apr 07 '17 at 13:23
  • Glad you got it sorted. :) *"I don't know how to upvote your answer as it's a comment and not a separate answer."* I haven't got the motivation to turn it into an answer. If you can, & let me know, I'll give it an upvote. Failing that, you might simply delete the question. – Andrew Thompson Apr 07 '17 at 13:43

2 Answers2

0

Here:

Thread.sleep(20);

You are most likely sleeping on the Event Dispatcher Thread. That will freeze your whole application. You have to step back and look into invokeLater to ensure "correct" threading within your UI.

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
0

Problem solved courtesy of @Andrew-Thompson:

"while(gameStarted) .. No, no a thousand times no. An infinite loop will likely freeze the GUI. Use a Swing based Timer to call those code statements in the loop, in the actionPerformed method of an ActionListener"