Flickering when updating overlapping JPanels inside a JLayeredPane using timestep

1k views Asked by At

I am making a game in Java. Basically, I have two different "planes" of updating that I need to take care of the. The base layer is the actual game painting itself. It is simply a JPanel that covers the entire JFrame, and is drawn to using its Graphics object.

I use a fixed timestep to take care of these first graphical updates. I have overwritten the paintComponent() method to do absolutely nothing, as I have written a custom render(float interpolation) method that takes care of that, as to prevent unwanted updates.

However, this panel can take no input beyond primitive mouse clicks and keyboard input. I need the ability to create various menus, text boxes, etc, that are also on the screen. Like various abilities, or even the "Menu" button that usually appears in the upper left corner of most games.

To take care of that input, such as creating buttons, I have a second JPanel that has setOpaque(false) applied to it. Then I create various Swing components that I might need, such as a JButton.

To contain the two JPanels, I use a JLayeredPane, and set their layers appropriately, as seen below. This way the input layer should always be on top of the actual game layer.

The code below shows how I create and add the Swing components to each other. addLoginDialog() is a method that adds a Swing component for the login. It has been tested and works properly, and isn't the problem.

private void initComponents()
{
    //This code is inside of the JFrame
    wholePane = new JLayeredPane();
    add(wholePane);
    guiPanel = new GUIPanel();
    guiPanel.setOpaque(false);
    gamePanel = new RPGPanel();
    gamePanel.setOpaque(false);
    wholePane.add(gamePanel, JLayeredPane.DEFAULT_LAYER);
    wholePane.add(guiPanel, JLayeredPane.POPUP_LAYER);
    guiPanel.addLoginDialog();
}

So when I run the code, I get horrible flickering. This is the code that is run from my fixed timestep ~60 times per second.

public void handleRepaint()
{
    //I don't use repaint() for the game drawing so I can be sure that fps is controlled.
    Graphics g = gamePanel.getGraphics();
    gamePanel.render(g);
    g.dispose();
    wholePane.repaint();
}

The problem is, I think, that the two different systems of updating the screen are clashing. The standard paintComponent() system is great for more static screens, but when I need to update consistently and keep track of the fps, I can't have updates going off randomly.

However, for the input layer, I only want to update as Swing normally does. When the mouse moves over a button, when I component is moved or is resized, etc.

Also, note the way the screen flickers: The Background image goes blank and then comes back again repeatedly. The input panel is always there, but is actually painted behind the game drawing, which shouldn't happen, because it is put in the default layer. The reason I know it isn't completely disappearing is because the game painting is partially transparent, so I can see underneath it, and the buttons I added are still there.

My main question is, how can I stop the flickering? Why is the game drawing being drawn on top of the input components when the game drawing is being done on the Panel that is in a lower layer in the JLayeredPane? And I supposed most importantly, what is causing the flickering? Thank you for any help.

1

There are 1 answers

7
MadProgrammer On

Why is the game drawing being drawn on top of the input components when the game drawing is being done on the Panel that is in a lower layer in the JLayeredPane?

Mostly because you've circumvented how Swing works.

Let's start with the fact that the Graphics context is a shared resource (typically there is a single Graphics context per native peer), that is, every component gets the same context, this means, when you use your own painting routine, you are basically painting over every thing else.

The RepaintManager is responsible for making decisions about what and when something should be painted. So what you now have is two artist fighting over the same canvas, wanting to paint on it simultaneously, which is just making a mess.

Add into the fray that Swing's painting process is not thread safe and you end up with a complete mess.

If you absolutely must have control, then you need to remove Swing's painting engine from the equation.

Start by taking a look at Passive vs. Active Rendering

ps- There is also hard ware acceleration benefits to using a BufferStrategy