Reputation: 1564
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...
Upvotes: 1
Views: 656
Reputation: 1564
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.
Upvotes: 1
Reputation: 347314
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
Upvotes: 2
Reputation: 763
You could either use the GlassPane or you could simply put a LayerUI on top, for example like this:
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();
}
}
}
Upvotes: 2
Reputation: 5860
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.
Upvotes: 0