1

I'm using javax.swing.JFrame to draw game animations using double buffer strategy. First, I set up the frame.

JFrame frame = new JFrame();
frame.setVisible(true);

Now, I draw an object (let it be a circle, doesn't matter) like this.

frame.createBufferStrategy(2);
bufferStrategy = frame.getBufferStrategy();
Graphics g = bufferStrategy.getDrawGraphics();
circle.draw(g);
bufferStrategy.show();

The problem is that the frame is not always fully set-up when the drawing takes place.

Seems like JFrame needs up to three steps in resizing itself, until it reaches it's final size. That makes the drawing slide out of frame or hinders it to appear completely from time to time.

I already managed to delay things using SwingUtilities.invokeLater(). While this improved the result, there are still times when the drawing slides away / looks prematurely draw.

Any idea / strategy? Thanks in advance.

EDIT: Ok thanks so far. I didn't mention that I write a little Pong game in the first place. Sorry for the confusion What I actually looked for was the right setup for accelerated game animations done in Java. While reading through the suggestions I found my question answered (though indirectly) here and this example made things clear for me.

A resume for this might be that for animating game graphics in Java, the first step is to get rid of the GUI logic overhead.

There's an tutorial article about the difference of passive and active rendering. That might help to clean a newbie's head...

Community
  • 1
  • 1
Jakob Hohlfeld
  • 1,503
  • 1
  • 17
  • 31
  • 4
    I'm curious: why are you drawing directly to the JFrame itself and not its contentPane? That is most unusual. And also, why use BufferStrategy to do your drawing? What's the context here? – Hovercraft Full Of Eels Nov 09 '12 at 17:06
  • 1
    @Jokel a JFrame is fully set up if created and made visible from the EDT (assuming that `setVisible(true)` is the last line you call). Are your creating that JFrame outside the EDT? Nevertheless, should you not find any solution to your issue, post an [SSCCE](http://sscce.org) which will allow us to truly help rather than making wild guesses. So far, I can't see any really helpful answers... – Guillaume Polet Nov 09 '12 at 19:13
  • I want to write some game animation. I don't want to use components and classes related to UI stuff. Still, I do need a (J)Frame object as a basic container, right? Drawing and updating should take place directly at the double buffer strategy. This approach comes from this [article](http://java.sys-con.com/node/46983). – Jakob Hohlfeld Nov 09 '12 at 19:29
  • 1
    The JFrame is just the top-level window, and while yes, you will need a top-level window of some sort, you should not do your animation or drawing directly in it. Instead I suggest that you extend JPanel, do your animation in it, and then add it to the JFrame so that it can be displayed. – Hovercraft Full Of Eels Nov 09 '12 at 22:08

4 Answers4

2

You could either use the GlassPane or you could simply put a LayerUI on top, for example like this:

A little something that scales

The circle rescales as you resize your frame.

import java.awt.*;
import java.awt.geom.Ellipse2D;

import javax.swing.*;
import javax.swing.plaf.LayerUI;

/**
 */
public class ALittleSomething {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("A Little Something");
                JLayer<JComponent> aLittleSomething = new JLayer<>(new ALittleSomethingPanel(), new ALittleSomethingUI());
                frame.getContentPane().add(aLittleSomething);
                frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                frame.setMinimumSize(new Dimension(800, 450));
                frame.setLocationRelativeTo(null); // Center
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    static class ALittleSomethingPanel extends JPanel {

        private JLabel aLittleSomethingLabel = new JLabel("A Little Something");

        ALittleSomethingPanel() {
            add(aLittleSomethingLabel);
            aLittleSomethingLabel.setFont(aLittleSomethingLabel.getFont().deriveFont(42f));
        }
    }

    static class ALittleSomethingUI extends LayerUI<JComponent> {

        @Override
        public void paint(Graphics g, JComponent c) {
            Graphics2D g2 = (Graphics2D) g.create();
            super.paint(g2, c);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            int height = c.getHeight();
            int width = c.getWidth();

            Shape circle = new Ellipse2D.Double(5, 5, width - 10, height - 10);

            g2.setPaint(new GradientPaint(15, 0, new Color(90, 97, 105), 15, 30, new Color(132, 142, 152)));
            g2.setStroke(new BasicStroke(5));
            g2.draw(circle);

            g2.dispose();
        }
    }
}
Skjalg
  • 763
  • 5
  • 13
2

You're better off drawing to a custom component and add this to your frame, start by having a look at How to Use Root Panes as to why it's a bad idea to draw directly to the frame (there's a lot of stuff already on top of it) then take a look at this and this and this for examples of (basic) animation done using Swing - all with just the default buffering strategy

You might also like to take a look at Performing Custom Painting and Graphics2D just as some primers

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
1

I'll answer this question myself because the question was a little misleading and I mixed things before I really knew the tools. The point I learned from this is, that for graphic programming in Java, there are two approaches: passive and active rendering.

While passive rendering happens from within the Event Dispatch Thread (EDT) and this is the process of letting the operating system and the VM determine the right time to draw components, it is not really possible or wanted to determine the exact moment when things get drawn. It's just not the right approach for creating animated game graphics.

What you need instead is the active rendering stargetey, as documented in articles like this one. There you disable the rendering event dispatching entirely and actively redraw the whole scene via buffer flipping or blitting yourself (see BufferStrategy).

The difference between pasive and active rendering in AWT/Swing is in short explained at java tutorials.

Jakob Hohlfeld
  • 1,503
  • 1
  • 17
  • 31
0

How about something like this:

private void createAndShowGUI()
{
  frame = new JFrame();
  frame.createBufferStrategy(2);
  bufferStrategy = frame.getBufferStrategy();
  Graphics g = bufferStrategy.getDrawGraphics();
  circle.draw(g);
  bufferStrategy.show();
  frame.setVisible(true);
}

And call that from the constructor or whatever:

public MyClass()
{
   SwingUtilities.invokeLater(new Runnable()
   {
      public void run()
      {
         createAndShowGUI();
      }
   });

   try
   {
      while(frame == null || !frame.isVisible())
         Thread.sleep(1);
   }
   catch(InterruptedException e)
   {
     e.printStackTrace();
     System.exit(1);
   }
}

Something like this, sorry I am posting this in a rush and some of my code might be wrong.

Vineet Kosaraju
  • 5,572
  • 2
  • 19
  • 21
  • Bucco, how am I guaranteed that JFrame has finished joggling? I can't see how `frame.setVisible(true)` would solve that. Besides, I won't be able to use double buffering while the frame is not visible. – Jakob Hohlfeld Nov 09 '12 at 19:05
  • 1
    ` while(frame == null || !frame.isVisible())Thread.sleep(1);` --> This is reallt not efficient. The proper way to go is to use `wait()` and `notify()` – Guillaume Polet Nov 09 '12 at 19:11