Background image covers the game - java

I just started learning Java and I am trying to develop a simple game (pretty much like Space Invaders). I am trying to set up a background image to my game instead of just setting a color for the background.
Problem: The background image overrides/overlaps my game. I can't see my game anymore. How do I fix this?
Relevant Codes:
public class Board extends JPanel implements Runnable
{
private Player player;
private Player2 player2;
private EnemyWave enemyWave;
private List<Guard> guards;
private boolean inGame;
private Integer lives;
private Integer lives2;
private String message;
Board()
{
JLabel backgroundImage;
inGame=true;
lives=3;
lives2=3;
player=new Player(START_X, START_Y);
player2 =new Player2(START_X-180, START_Y);
enemyWave = new EnemyWave();
guards = new ArrayList<>();
for(int i=0; i<6 ; i++) {
guards.add(new Guard(GUARD_POSX + i * 125, GUARD_POSY));
}
addKeyListener(new KAdapter());
setFocusable(true);
//setBackground(new Color(168, 219, 127, 116)); // Background color
// BACKGROUND IMAGE
ImageIcon img = new ImageIcon("background.jpg");
backgroundImage = new JLabel("",img,JLabel.CENTER);
backgroundImage.setBounds(0,0,1200,700);
add(backgroundImage);
setVisible(true);
}
#Override
public void addNotify() {
super.addNotify();
Thread animator = new Thread(this);
animator.start();
}
#Override
public void run() {
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while(inGame) {
repaint();
animationCycle(); //mechanics of a game
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if(sleep<0) {
sleep = 2;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
beforeTime=System.currentTimeMillis();
}
gameOver();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Font font = new Font("Verdana", Font.PLAIN, 18);
g.setColor(Color.RED);
g.setFont(font);
g.drawString("Player 1 Lives: " + lives.toString(), BOARD_WIDTH - 185, 25);
g.drawString("Enemies Left: " + enemyWave.getNumberOfEnemies().toString(), 28, 25);
g.drawString("Player 2 Lives: " + lives2.toString(), BOARD_WIDTH - 185, 55);
g.setColor(Color.GREEN); // border color where the plane lies
g.drawLine(0, GROUND, BOARD_WIDTH, GROUND);
player.draw(g, this);
if (player.getM().isVisible())
player.getM().draw(g, this);
//create
player2.draw(g, this);
if (player2.getM().isVisible())
player2.getM().draw(g, this);
enemyWave.draw(g, this);
for (Guard guard : guards) {
guard.draw(g);
}
}
}
Before setting background image:
After setting background image (completely covered my game):

Swing has a parent/child relationship.
So a component will:
first paint itself based on the painting logic found in the paintComponent(...) method.
then paint any child components added to the panel.
In the constructor of your class you have:
add(backgroundImage);
which add the component to the panel, meaning it will paint on top of your custom painting.
Don't use a JLabel for your background.
Just paint the Image as the first step in your paintComponent(...) method.

Added this in my Board class:
Image bg = Toolkit.getDefaultToolkit().getImage("bg.jpg");
Added this in my paintComponent method:
g.drawImage(bg, 0, 0, null);
And it worked!

Related

Weird issue updating a JLabel on JSlider change

Huy guys, I have a weird problem.
I'm kinda new to swing and java applications.
I'm trying to make a custom UI jslider that changes a jlabel text when you move the thumb.
My problem is: when I use the addChangeListener(), it creates a weird glitch when moving the thumb.
If I don't use it, it works perfectly fine.
How can I update the JLabel without using the change listener or how can I fix this graphic bug?
Most of this code comes from stackoverflow since I don't know much about painting components.
See pictures at the button to better understand the problem
Thanks!
The code in my jpanel
JSlider slider = new JSlider(SwingConstants.HORIZONTAL, 1, 10, 1) {
#Override
public void updateUI() {
setUI(new CustomSliderUI(this));
}
};
// If I comment this line the visual glitch is gone when moving the thumb, but the value doesnt update
slider.addChangeListener((event) -> RAM_LABEL.setText(slider.getValue() + " Gb"));
slider.setMinorTickSpacing(1);
slider.setMajorTickSpacing(10);
slider.setSnapToTicks(true);
slider.setBounds(96, 317, 300, 35);
slider.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
slider.setOpaque(false);
this.add(slider);
The custom slider ui class
private static class CustomSliderUI extends BasicSliderUI
{
private static final int TRACK_HEIGHT = 8;
private static final int TRACK_ARC = 5;
private static final Dimension THUMB_SIZE = new Dimension(22, 20);
private final RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float();
private final Image knob;
public CustomSliderUI(final JSlider b)
{
super(b);
knob = Swinger.getResource("knob.png");
}
#Override
protected void calculateTrackRect() {
super.calculateTrackRect();
trackRect.y = trackRect.y + (trackRect.height - TRACK_HEIGHT) / 2;
trackRect.height = TRACK_HEIGHT;
trackShape.setRoundRect(trackRect.x, trackRect.y, trackRect.width, trackRect.height, TRACK_ARC, TRACK_ARC);
}
#Override
protected void calculateThumbLocation() {
super.calculateThumbLocation();
thumbRect.y = trackRect.y + (trackRect.height - thumbRect.height) / 2;
}
#Override
protected Dimension getThumbSize() {
return THUMB_SIZE;
}
#Override
public void paint(final Graphics g, final JComponent c) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paint(g, c);
}
#Override
public void paintTrack(final Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Shape clip = g2.getClip();
boolean inverted = slider.getInverted();
// Paint shadow.
g2.setColor(new Color(170, 170 ,170));
g2.fill(trackShape);
// Paint track background.
g2.setColor(new Color(200, 200 ,200));
g2.setClip(trackShape);
trackShape.y += 1;
g2.fill(trackShape);
trackShape.y = trackRect.y;
g2.setClip(clip);
// Paint selected track.
boolean ltr = slider.getComponentOrientation().isLeftToRight();
if (ltr) inverted = !inverted;
int thumbPos = thumbRect.x + thumbRect.width / 2;
if (inverted) {
g2.clipRect(0, 0, thumbPos, slider.getHeight());
} else {
g2.clipRect(thumbPos, 0, slider.getWidth() - thumbPos, slider.getHeight());
}
g2.setColor(Swinger.getTransparentWhite(0));
g2.fill(trackShape);
g2.setClip(clip);
}
#Override
public void paintThumb(final Graphics g)
{
g.drawImage(knob, thumbRect.x, thumbRect.y, null);
}
#Override
public void paintFocus(final Graphics g) {}
}
Visual glitch when you are moving the cursor, as soon as you stop pressing the mouse it goes back to normal
Regular cursor / when I move it without the change listener
Solved. I went with drawing my own background it was simplier.
private static class CustomSliderUI extends BasicSliderUI
{
private static final Dimension THUMB_SIZE = new Dimension(22, 20);
private final Image thumb, background;
private final JSlider slider;
public CustomSliderUI(final JSlider b)
{
super(b);
slider = b;
thumb = Swinger.getResource("thumb.png");
background = Swinger.getResource("slider.png");
}
#Override
protected Dimension getThumbSize() {
return THUMB_SIZE;
}
#Override
public void paintTrack(final Graphics g) {
g.drawImage(background, 10, 11, 280, 10, null);
}
#Override
public void paintThumb(final Graphics g)
{
g.drawImage(thumb, thumbRect.x, thumbRect.y, null);
slider.repaint();
}
#Override
public void paintFocus(final Graphics g) {}
}
JSlider slider = new JSlider(SwingConstants.HORIZONTAL, 1, 10, 1) {
#Override
public void updateUI() {
setUI(new CustomSliderUI(this));
}
};
slider.addChangeListener((event) -> RAM_LABEL.setText(slider.getValue() + " Gb"));
slider.setBounds(96, 317, 300, 35);
slider.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
slider.setOpaque(false);
this.add(slider);

How to make a JPanel reset?

I have a simple game of pong where when the user clicks a JButton which is displayed on the JPanel it should reset the game. How can I do this? I was thinking just remove the JPanel and add a new one (the JPanel contains all of the necessary code/class references for the game) I tried writing this however, and it didn't work, nothing happens. Here is my code:
JFrame Class:
public class Window extends JFrame implements ActionListener {
static int length = 1000;
static int height = 1000;
Display display = new Display();
Window() {
setTitle("Program Display");
setSize(length + 22, height + 40);
setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton restart = new JButton("Start New Game");
add(display);
display.add(restart);
restart.addActionListener(this);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
remove(display);
Display display2 = new Display();
JButton restart = new JButton("Start New Game");
add(display2);
display2.add(restart);
restart.addActionListener(this);
revalidate();
repaint();
}
}
JPanel Class:
public class Display extends JPanel implements ActionListener {
int up = 0;
int down = 500;
double ballx = 500;
double bally = 500;
char ballDirection;
Rectangle border;
static Rectangle borderEast;
static Rectangle borderNorth;
static Rectangle borderSouth;
static Rectangle borderWest;
static boolean gameOver;
Timer timer;
Paddle p;
Ball b;
Display() {
p = new Paddle();
b = new Ball();
up = p.up;
down = p.down;
ballx = b.ballx;
bally = b.bally;
ballDirection = b.ballDirection;
initTimer();
b.startBall();
addKeyListener(p);
setFocusable(true);
}
public void initTimer() {
timer = new Timer(10, this);
timer.start();
}
public void setUpBorders(Graphics2D g2d) {
border = new Rectangle(0, 0, Window.length, Window.height);
borderEast = new Rectangle(Window.length, 0, 2, Window.height);
borderWest = new Rectangle(0, 0, 2, Window.height);
borderSouth = new Rectangle(0, Window.height, Window.length, 2);
borderNorth = new Rectangle(0, 0, Window.length, 2);
g2d.setColor(Color.RED);
g2d.draw(border);
}
public void paintPaddle(Graphics2D g2d) {
g2d.setColor(new Color(0, 130, 130));
g2d.fill(p.paddle);
}
public void paintBall(Graphics2D g2d) {
g2d.setColor(new Color(0, 130, 130));
g2d.fillOval((int) ballx, (int) bally, 20, 20);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.BLACK);
Graphics2D g2d = (Graphics2D) g;
setUpBorders(g2d);
paintPaddle(g2d);
paintBall(g2d);
if(gameOver == true) {
Font custom = new Font("Dialog", Font.BOLD, 60);
g2d.setColor(Color.RED);
g2d.setFont(custom);
g2d.drawString("Game Over. Your score was: " + Ball.score + "!", 50, 500);
}
}
public void checkBorderHit() {
b.checkBorderHit();
p.checkBorderHit();
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
up = p.up;
down = p.down;
ballx = b.ballx;
bally = b.bally;
ballDirection = b.ballDirection;
b.moveBall();
checkBorderHit();
repaint();
}
}
You didn't say what exactly doesn't work, but you forgot to call revalidate() and repaint() after adding display2. If your problem was that after pressing the button nothing happens, this will probably solve it.
Edit:
We still can't run you code, because the Ball and Paddle class are missing (my mistake for not mentioning that), but try to set important variables like gameOver false when first "mentioning them" (I don't know the proper term, just do static boolean gameOver = false; instead of static boolean gameOver;). Do this in all other classes also. Sorry for not saying which variables you exactly must change, but I'm not saying anything I'm not 100% sure about without being able to test it :P (maybe a more experienced person can help you more)

When i add code to paintComponent() my gui disappears

In my code after i added code to paintComponent() when i run it, all the JLabel, textfields, and buttons disappear the textfields and buttons reappear when i click on them while the program is running but i still can't see any of the JLabels.
I hope it is something silly i have commented out the code in the paintComponent() method that seems to cause this error.
public class snowBoarding extends JFrame {
private JButton getReset() {
if (Reset == null) {
Reset = new JButton();
Reset.setBounds(new Rectangle(162, 411, 131, 39));
Reset.setFont(new Font("Dialog", Font.BOLD, 18));
Reset.setText("Reset");
Reset.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e){
textField_1.setText("0");
textField_2.setText("0");
textField_3.setText("0");
textField_4.setText("0");
textField_5.setText("0");
textField_6.setText("0");
textField_7.setText("0");
textField_8.setText("0");
textField_9.setText("0");
textField_10.setText("0");
textField_11.setText("0");
textField.setText("0");
total_1.setText("0");
total_2.setText("0");
Overall.setText("0");
DrawPanel.clear(DrawPanel.getGraphics());
DrawPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
}
});
}
return Reset;
}
public JButton getButton_calc_draw() {
if (Button_calc_draw == null) {
Button_calc_draw = new JButton();
Button_calc_draw.setBounds(303, 411, 131, 39);
Button_calc_draw.setFont(new Font ("Dialog", Font.BOLD, 18));
Button_calc_draw.setText("Draw");
Button_calc_draw.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
// Get values from the text fields
run_1[0] = Integer.parseInt(textField.getText());
run_1[1] = Integer.parseInt(textField_1.getText());
run_1[2] = Integer.parseInt(textField_2.getText());
run_1[3] = Integer.parseInt(textField_3.getText());
run_1[4] = Integer.parseInt(textField_4.getText());
run_1[5] = Integer.parseInt(textField_5.getText());
for (int i = 0; i < run_1.length; i++) {
temp[i] = run_1[i];
}
Arrays.sort(temp);
for (int i = 1; i < (temp.length -1) ; i++){
avg1+=temp[i];
}
avg1 = avg1/4;
run_2[0] = Integer.parseInt(textField_6.getText());
run_2[1] = Integer.parseInt(textField_7.getText());
run_2[2] = Integer.parseInt(textField_8.getText());
run_2[3] = Integer.parseInt(textField_9.getText());
run_2[4] = Integer.parseInt(textField_10.getText());
run_2[5] = Integer.parseInt(textField_11.getText());
for (int i = 0; i < run_2.length; i++) {
temp[i] = run_2[i];
}
Arrays.sort(temp);
for (int i = 1; i < (temp.length -1) ; i++){
avg2+=temp[i];
}
avg2 = avg2/4;
if (avg1 > avg2){
OverallScore = avg1;
}
else {
OverallScore = avg2;
}
total_1.setText(Integer.toString(avg1));
total_2.setText(Integer.toString(avg2));
Overall.setText(Integer.toString(OverallScore));
DrawPanel.repaint();
}
// Transfer the image from the BufferedImage to the JPanel to make it visible.
;
});
}
return Button_calc_draw;
}
}
});
}
return Reset;
}
private myJPanel getDrawPanel() {
if (DrawPanel == null) {
DrawPanel = new myJPanel();
DrawPanel.setLayout(new GridBagLayout());
DrawPanel.setBounds(new Rectangle(258, 39, 326, 361));
DrawPanel.setBackground(Color.white);
DrawPanel.setEnabled(true);
DrawPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
//Instantiate the BufferedImage object and give it the same width
// and height as that of the drawing area JPanel
img = new BufferedImage(DrawPanel.getWidth(),
DrawPanel.getHeight(),
BufferedImage.TYPE_INT_RGB);
//Get its graphics context. A graphics context of a particular object allows us to draw on it.
g2dImg = (Graphics2D)img.getGraphics();
//Draw a filled white coloured rectangle on the entire area to clear it.
g2dImg.setPaint(Color.WHITE);
g2dImg.fill(new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
}
return DrawPanel;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
snowBoarding thisClass = new snowBoarding();
thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
thisClass.setVisible(true);
}
});
}
public snowBoarding() {
super();
setResizable(false);
getContentPane().setLayout(null);
initialize();
}
private void initialize() {
this.setSize(600, 500);
this.setContentPane(getJContentPane());
this.setTitle("Snowboarding Score Calculator");
this.setResizable(false);
this.setVisible(true);
}
}
class myJPanel extends JPanel {
BufferedImage img;
Graphics2D g2dImg;
private static final long serialVersionUID = 1L;
private Rectangle2D.Double rectangle;
public void paintComponent(Graphics g) {
//Must be called to draw the JPanel control.
// As a side effect, it also clears it.
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
rectangle = new Rectangle2D.Double(0, 260-score[0] * 2, 25, score[0] * 2);
g2D.setPaint(Color.blue);
g2D.fill(rectangle);
g2D.draw(rectangle);
}
protected void clear(Graphics g) {
super.paintComponent(g);
// Also clear the BufferedImage object by drawing a white coloured filled rectangle all over.
g2dImg.setPaint(Color.WHITE);
g2dImg.fill(new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
}
}
edit: removing unnecessary code
i need the repaint to draw rectangles using the run_1 and the run_2 array as the x or y values after i click draw and the reset to return the painted image back to a white slate.
draw button --> draws the graph
reset button --> removes the graph so that a new graph can be created.
You shall not use g2dImg in paintComponent(), but g instead (the parameter received by method paintComponent()). More precisely, ((Grpahics2D)g) instead of g2dImg.
g2dImg doesn't seem to be initialized in your code posted here, maybe you have done it somewhere...
More generally, you shall always use the Graphics instance you received in paint methods (casting it to Graphics2D if needed). You shall not try to reuse/share/store instances of Graphics.
The same applies for the clear() method.
Here is an example of how to rewrite this paintComponent() method:
private boolean shallPaint = false;
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (shallPaint) {
Graphics2D g2D = (Graphics2D) g;
rectangle = new Rectangle2D.Double(0, 260-score1 * 2, 25, score1 * 2);
g2D.setPaint(Color.blue);
g2D.fill(rectangle);
g2D.draw(rectangle);
}
}
public void setShallPaint(boolean pShallPaint) {
shallPaint = pShallPaint;
}
Then simply call myJPanel.repaint() to repaint it.
You shall replace, in your reset button:
DrawPanel.clear(DrawPanel.getGraphics());
with:
DrawPanel.setShallPaint(false);
DrawPanel.repaint();
And in Button_calc_draw:
DrawPanel.setShallPaint(true);
DrawPanel.repaint();

Proper way to use JLabels to update an image

I am creating a GUI, and am fairly new to swing and awt. I am trying to create a gui that, upon launch, sets the background to an image, then uses a method to create a slideshow of sorts. I have attempted it, and I am not attached to the code so I am able to take both revisions and/or whole new concepts.
EDIT(9/15/13): I am having trouble with the slideshow, I cant seem to get it to work.
Here is my current code.
public class MainFrame extends JFrame{
JLabel backgroundL = null;
private JLabel bakckgroundL;
BufferedImage backimg;
Boolean busy;
double width;
double height;
public MainFrame() throws IOException {
initMainframe();
}
public void initMainframe() throws IOException {
//misc setup code, loads a default jpg as background
setTitle("Pemin's Aura");
busy = true;
String backgroundDir = "resources/frame/background.jpg";
backimg = ImageIO.read(new File(backgroundDir));
backgroundL = new JLabel(new ImageIcon(backimg));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
refreshframe();
setVisible(true);
busy = false;
}
public void adjSize() { // the attempted start of a fullscreen mode
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
width = this.getWidth();
height = this.getHeight();
setVisible(true);
}
public void setmastheadText() {//unfinished code
busy = true;
busy = false;
}
public void setbackground() {
add(backgroundL);
}
public void refreshframe() { //should refresh image?
setSize(2049, 2049);
setSize(2048, 2048);
}
public void loadingscreen() throws IOException, InterruptedException {
//this is the code in question that is faulty.
if (busy == false) {
busy = true;
String backgroundDir1 = "resources/frame/background.jpg";
String backgroundDir2 = "resources/frame/scr1.jpg";
String backgroundDir3 = "resources/frame/scr2.jpg";
BufferedImage backimg1 = ImageIO.read(new File(backgroundDir1));
BufferedImage backimg2 = ImageIO.read(new File(backgroundDir2));
BufferedImage backimg3 = ImageIO.read(new File(backgroundDir3));
backgroundL = new JLabel(new ImageIcon(backimg1));
Thread.sleep(2000);
setbackground();
setVisible(true);
backgroundL = new JLabel(new ImageIcon(backimg2));
setbackground();
setVisible(true);
Thread.sleep(2000);
bakckgroundL = new JLabel(new ImageIcon(backimg3));
setbackground();
setVisible(true);
if(backimg != null) {
backgroundL = new JLabel(new ImageIcon(backimg));;
}
}
busy = false;
}//end of loading screen
See ImageViewer for a working example of displaying images using a Swing based Timer.
See also How to use Swing Timers.
And while I'm here, another (prettier) example of animating an image. It uses this Mercator map of land masses. The image can be tiled horizontally, and therefore be scrolled left/right as needed.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class WorldView {
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/P59NF.png");
final BufferedImage bi = ImageIO.read(url);
Runnable r = new Runnable() {
#Override
public void run() {
int width = 640;
int height = 316;
Graphics2D g = bi.createGraphics();
float[] floats = new float[]{0f, .4f, .55f, 1f};
Color[] colors = new Color[]{
new Color(20, 20, 20, 0),
new Color(0, 10, 20, 41),
new Color(0, 10, 20, 207),
new Color(0, 10, 20, 230),};
final LinearGradientPaint gp2 = new LinearGradientPaint(
new Point2D.Double(320f, 0f),
new Point2D.Double(0f, 0f),
floats,
colors,
MultipleGradientPaint.CycleMethod.REFLECT);
final BufferedImage canvas = new BufferedImage(
bi.getWidth(), bi.getHeight() + 60,
BufferedImage.TYPE_INT_RGB);
final JLabel animationLabel = new JLabel(new ImageIcon(canvas));
ActionListener animator = new ActionListener() {
int x = 0;
int y = 30;
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g = canvas.createGraphics();
g.setColor(new Color(55, 75, 125));
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
int offset = (x % bi.getWidth());
g.drawImage(bi, offset, y, null);
g.drawImage(bi, offset - bi.getWidth(), y, null);
g.setPaint(gp2);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
g.dispose();
animationLabel.repaint();
x++;
}
};
Timer timer = new Timer(40, animator);
timer.start();
JOptionPane.showMessageDialog(null, animationLabel);
timer.stop();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Here is a version of that image with the equator added (it is 44 pixels 'south' of the center of the image).
You're calling Thread.sleep(...) and likely on the EDT or Swing event thread (full name is the Event Dispatch Thread). This thread is responsible for all Swing painting/drawing and user interactions, and so sleeping it will only serve to freeze your entire GUI. Instead you should use a Swing Timer to allow you to swap a JLabel's ImageIcon.
So, briefly:
Don't call Thread.sleep(...) on the Swing event thread (Event Dispatch Thread or EDT).
Do use a Swing Timer to do your repeating delayed actions.
Don't make and add many JLabels. Just make and add one.
Do Swap the ImageIcon that the JLabel displays by calling setIcon(...) on the label.
Better (cleaner) to write if (busy == false) { as if (!busy) {
e.g.,
ImageIcon[] icons = {...}; // filled up with your ImageIcons
if (!busy) {
int timerDelay = 2000;
new Timer(timerDelay, new ActionListener() {
private int i = 0;
public void actionPerfomed(ActionEvent e) {
myLabel.setIcon(icons(i));
i++;
if (i == icons.length) {
((Timer)e.getSource).stop();
}
};
}).start();
}

Java Panel Double Buffering

wondered if anyone could point me in the right directon, i have developed a pong game and it needs double buffering due to flickering. Iv tryed some of the post on here to try and make it work, but im still a beginner with the swing awt suff, any help would be amazing thanks.
public class PongPanel extends JPanel implements Runnable {
private int screenWidth = 500;
private int screenHeight = 300;
private boolean isPaused = false;
private boolean isGameOver = false;
private int playToPoints = 10;
private Padel player1,player2;
private Ball ball;
private Thread gameThread;
private Image dbImage;
private Graphics dbg;
public PongPanel() {
setPreferredSize(new Dimension(screenWidth,screenHeight));
setBackground(Color.BLACK);
setDoubleBuffered(true);
setFocusable(true);
requestFocus();
player1 = new Padel(Position.LEFT,screenWidth,screenHeight);
player2 = new Padel(Position.RIGHT,screenWidth,screenHeight);
ball = new Ball(10,screenWidth/2,screenHeight/2,Color.WHITE);
}
public void addNotify(){
super.addNotify();
startGame();
}
private void startGame(){
gameThread = new Thread(this);
gameThread.start();
}
#Override
public void run() {
while (!isGameOver) {
dbImage = createImage(screenWidth,screenHeight);
dbg = this.getGraphics();
if(!isPaused){
if(!gameOverCheck()){
updateGame();
paintComponents(dbg);
}
}else if(isPaused){
dbg.setColor(Color.ORANGE);
dbg.setFont(new Font("serif",Font.BOLD,50));
dbg.drawString("Paused", screenWidth/2-82, screenHeight/2);
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {e.printStackTrace();}
}
}
private boolean gameOverCheck(){
if(player1.getScore() == playToPoints){
dbg.setColor(player1.getColour());
dbg.setFont(new Font("serif",Font.BOLD,50));
dbg.drawString("Player 1 Wins!", screenWidth/2 - 161, screenHeight/2);
setGameOver(true);
return true;
}else if(player2.getScore() == playToPoints){
dbg.setColor(player2.getColour());
dbg.setFont(new Font("serif",Font.BOLD,50));
dbg.drawString("Player 2 Wins!", screenWidth/2 - 161, screenHeight/2);
setGameOver(true);
return true;
}
return false;
}
private void updateGame(){
ball.move(screenWidth,screenHeight,player1,player2);
player1.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
player2.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
}
#Override
public void paintComponents(Graphics g) {
super.paintComponents(g);
dbg.setColor(Color.BLACK);
dbg.fillRect(0, 0, screenWidth+20, screenHeight+20);
dbg.setColor(Color.WHITE);
dbg.drawLine(screenWidth/2, 0, screenWidth/2, screenHeight);
dbg.setFont(new Font("serif",Font.BOLD,32));
dbg.drawString(player1.getScore()+"", screenWidth/2-40, screenHeight - 20);
dbg.drawString(player2.getScore()+"", screenWidth/2+20, screenHeight - 20);
ball.drawBall(dbg);
player1.drawPadel(dbg);
player2.drawPadel(dbg);
}
}
There's a really good tutorial here which describes how to use BufferStrategy to produce non-flickering animation.
The important points are:
Call setIgnoreRepaint(true) on the top-level Canvas to prevent AWT from repainting it, as you'll typically be doing this yourself within the animation loop.
Obtain the Graphics2D object from the BufferStrategy (rather than using the instance passed in via paintComponent(Graphics g).
A must-read about the painting mechanism in AWT and Swing
Painting in AWT and Swing
The basic problem is, you're violating the basic painting system of Swing. Swing uses a "passive rendering" algorithm, where paints are performed only when they need to be. You can make suggestions to the API about when something should be update via the repaint call.
Based on your code, the basic problem is, you're calling paintComponents with your own Graphics context, but the system is is then trashing it with it's paint paint pass, you are fighting the paint system rather then working with it.
If done correctly, Swing components are already double buffered, so you don't need to do anything "extra", other then actual work with the API/system.
I strongly recommend having a look at:
Performing Custom Painting
Painting in AWT and Swing
to get a better understanding of how painting works in Swing.
So, let's start with...
#Override
public void run() {
while (!isGameOver) {
dbImage = createImage(screenWidth, screenHeight);
dbg = this.getGraphics();
if (!isPaused) {
if (!gameOverCheck()) {
updateGame();
paintComponents(dbg);
}
} else if (isPaused) {
dbg.setColor(Color.ORANGE);
dbg.setFont(new Font("serif", Font.BOLD, 50));
dbg.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2);
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Swing is NOT thread safe, you should NOT be updating the UI from outside the context of the Event Dispatching Thread
You should NEVER call any paint method directly. The system will perform this operation when it wants to update your component.
I would strongly recommend having a read of:
Concurrency in Swing
How to Use Swing Timers for a possible solution
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.text.Position;
public class PongPanel extends JPanel implements Runnable {
private int screenWidth = 500;
private int screenHeight = 300;
private boolean isPaused = false;
private boolean isGameOver = false;
private int playToPoints = 10;
private Padel player1, player2;
private Ball ball;
private Timer gameThread;
public PongPanel() {
setPreferredSize(new Dimension(screenWidth, screenHeight));
setBackground(Color.BLACK);
setDoubleBuffered(true);
setFocusable(true);
requestFocus();
player1 = new Padel(Position.LEFT, screenWidth, screenHeight);
player2 = new Padel(Position.RIGHT, screenWidth, screenHeight);
ball = new Ball(10, screenWidth / 2, screenHeight / 2, Color.WHITE);
}
public void addNotify() {
super.addNotify();
startGame();
}
private void startGame() {
gameThread = new Timer(30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateGame();
gameOverCheck();
if (isGameOver) {
repaint();
return;
}
if (!isPaused) {
if (!gameOverCheck()) {
updateGame();
}
}
repaint();
}
});
gameThread.start();
}
private boolean gameOverCheck() {
if (player1.getScore() == playToPoints) {
setGameOver(true);
return true;
} else if (player2.getScore() == playToPoints) {
setGameOver(true);
return true;
}
return false;
}
private void updateGame() {
ball.move(screenWidth, screenHeight, player1, player2);
player1.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
player2.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, screenWidth + 20, screenHeight + 20);
g2d.setColor(Color.WHITE);
g2d.drawLine(screenWidth / 2, 0, screenWidth / 2, screenHeight);
g2d.setFont(new Font("serif", Font.BOLD, 32));
g2d.drawString(player1.getScore() + "", screenWidth / 2 - 40, screenHeight - 20);
g2d.drawString(player2.getScore() + "", screenWidth / 2 + 20, screenHeight - 20);
ball.drawBall(g2d);
player1.drawPadel(g2d);
player2.drawPadel(g2d);
if (isGameOver) {
if (player1.getScore() == playToPoints) {
g2d.setColor(player1.getColour());
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Player 1 Wins!", screenWidth / 2 - 161, screenHeight / 2);
} else if (player2.getScore() == playToPoints) {
g2d.setColor(player2.getColour());
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Player 2 Wins!", screenWidth / 2 - 161, screenHeight / 2);
}
} else if (isPaused) {
g2d.setColor(Color.ORANGE);
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2);
}
g2d.dispose();
}
}
BufferedStrategy
Has already been suggested, BufferStrategy is a viable solution in cases where you want to take complete control off the painting system. It's more complex, but gets you around the oddities of the passive rendering system used by Swing
I think you can just call super(true);
first thing, and this just tells the JPanel that it is double buffered... because one of JPanel's constructors is:
public Jpanel(boolean isDoubleBuffered) {...}
I hope this helps someone even though it is nearly four years later.

Categories