I've had the flickering problem for a while and I would like to know if there is a simple way to solve the problem. I know you can use the BufferStrategy but I can not find a good, updated source that work for.png images. The flickering occures when I move my character or when the repaint() method is called.
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class Game extends JFrame implements Runnable, KeyListener {
private static final long serialVersionUID = 1L;
Image back = new ImageIcon(Game.class.getResource("LEVEL1.png")).getImage();
Image player = new ImageIcon(Game.class.getResource("freddyD.png")).getImage();
Image studio = new ImageIcon(Game.class.getResource("studio0.png")).getImage();
public int xPos = 100;
public int yPos = 300;
public boolean D = false;
public boolean Q = false;
public Game() {
addKeyListener(this);
setSize(900, 700);
setLocationRelativeTo(null);
setVisible(true);
}
public void paint(Graphics g) {
g.drawImage(back, 0, 0, 6000, 6000, this);
g.drawImage(studio, 100, 100, 340, 310, this);
g.drawImage(player, xPos, yPos, 60, 104, this);
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
int k = e.getKeyCode();
if (k == KeyEvent.VK_D) {
D = true;
}
if (k == KeyEvent.VK_Q) {
Q = true;
}
}
public void keyReleased(KeyEvent e) {
int k = e.getKeyCode();
if (k == KeyEvent.VK_D) {
D = false;
}
if (k == KeyEvent.VK_Q) {
Q = false;
}
}
public void run() {
while (true) {
if (D) {
xPos += 2;
repaint();
}
if (Q) {
xPos -= 2;
repaint();
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) {
new Game().start();
}
public void start() {
new Thread(this).start();
}
}
This is a relative common issue. Top level containers, like JFrame
, are not double buffered. This means that each change made to the graphics context will be applied immediately, which is causing the flickering.
As a general rule of thumb you shouldn't:
paint
of any componentJFrame
is actually a compound component, mean it has several other child components which are used to generate the general layout, and, because of the way the painting sub system works, these child components can be painted independently (or without notification to) the frame.
Start by reading through Performing Custom Painting and Painting in AWT and Swing for some more details about how painting in Swing works and how you should work with it.
As a general recommendation, you should start with a JPanel
and override it's paintComponent
method (don't forget to call super.paintComponent
)
Remember, Swing is not thread safe, so you need to take that into consideration. You might consider using something like aSwing Timer instead of a Thread
, if the frame rate is not super critical. A Swing Timer
will ensure that all "tick" events take place in the Event Dispatching Thread, making it safer to update the UI (and the state the UI depends on).
If you need more direct control, then you will need to consider using a BufferStrategy
, which will give you direct control over when something is painted, allowing you to better control the painting process.
KeyListener
is generally a poor choice for this type of task, for a number of reasons. Instead you should be using Key Bindings API which solves these short comings.
This is just a simple paint example to get you started...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Game {
public static void main(String[] args) {
new Game();
}
public Game() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Rectangle hBox;
private Rectangle vBox;
private int hDelta = 1;
private int vDelta = 1;
public TestPane() {
hBox = new Rectangle(0, 0, 10, 10);
vBox = new Rectangle(0, 0, 10, 10);
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
hBox.x += hDelta;
hBox.y = (getHeight() - 10) / 2;
if (hBox.x + hBox.width >= getWidth()) {
hBox.x = getWidth() - hBox.width;
hDelta *= -1;
} else if (hBox.x <= 0) {
hBox.x = 0;
hDelta *= -1;
}
vBox.y += vDelta;
vBox.x = (getWidth() - vBox.width) / 2;
if (vBox.y + vBox.height >= getHeight()) {
vBox.y = getHeight() - vBox.height;
vDelta *= -1;
} else if (vBox.y <= 0) {
vBox.y = 0;
vDelta *= -1;
}
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(hBox);
g2d.setColor(Color.BLUE);
g2d.fill(vBox);
g2d.dispose();
}
}
}
The thing I always do that doesn't take that much time is just increase the time in Thread.sleep()
. The golden number that I found to work while being low is 15 millis; anything lower than that starts to flicker. In your code, you use 10 millis, which might be the issue. If you don't want to do that (for speed reasons or whatever), use BufferedImage
. It's pretty complicated, but if you want to know more here is a good site . The main thing is that BufferedImage
.
This kind of issues can happen when you use single buffer for drawing. Check double buffering, it should solve your problem: https://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.