5

I have been following the Java Game Programming for Beginners tutorial series, and wished to experiment by applying a background image. Unfortunately, when I render it through the paintComponent method, it moves with my sprite (albeit at one unit continuously as opposed to five); and when I render it through the paint method, I get a strange, flickering box that matches the color designated in the setBackground (color) property of the JFrame and it moves with the sprite identically to that of the prior instance (within paintComponent).

How might I code the image so as to remain static, as a background should be?

Code:

public class JavaGame extends JFrame{

int x, y;
private Image dbImage;
private Graphics dbg;
Image ghost;
Image bg;


public class AL extends KeyAdapter{
    public void keyPressed(KeyEvent e){
        int keyCode = e.getKeyCode();

        if(keyCode == e.VK_LEFT){
            if(x <= 8)
                x = 8;
            else
                x += -5;
        }
        if(keyCode == e.VK_RIGHT){
            if(x >= 235)
                x = 235;
            else
                x += +5;
        }
        if(keyCode == e.VK_UP){
            if(y <= 18)
                y = 18;
            else
                y += -5;
        }
        if(keyCode == e.VK_DOWN){
            if(y >= 235)
                y = 235;
            else
                y += +5;
        }
    }
    public void keyReleased(KeyEvent e){

    }

}

public JavaGame(){
    //Load images
    ImageIcon i = new ImageIcon("C:/Users/Taylor/workspace/Java game/src/ghost.png");
    ghost = i.getImage();

    ImageIcon j = new ImageIcon("C:/Users/Taylor/workspace/Java game/src/bg.png");
    bg = j.getImage();

    //Game properties
    addKeyListener(new AL());
    setTitle("Java Game");
    setSize(500, 500);
    setResizable(false);
    setVisible(true);
    setBackground(Color.GRAY);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    x = 150;
    y = 150;
}

public void paint(Graphics g){
    g.drawImage(bg, 0, 0, null);

    dbImage = createImage(getWidth(), getHeight());
    dbg = dbImage.getGraphics();
    paintComponent(dbg);
    g.drawImage(dbImage, x, y, this);
}

public void paintComponent(Graphics g){
    g.setColor(Color.WHITE);
    g.drawImage(ghost, x, y, this);

    repaint();
}


public static void main(String[] args) {
    new JavaGame();

}

Pictures:

rendered through 'paint' rendered through 'paintComponent'

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
tadamson
  • 381
  • 1
  • 3
  • 8
  • 2
    In general, when you take a screen shot, you should edit them to include only the relevant portion (IE, the game window). – Jeffrey Aug 22 '12 at 23:53
  • 1
    Don't call `repaint` from `paintComponent`. `repaint` (eventually) calls `paint` which calls `paintComponent` which calls `repaint`, it's an infinite loop. – Jeffrey Aug 22 '12 at 23:55
  • @Jeffrey As mentioned in [How do I create screenshots?](http://meta.stackexchange.com/questions/99734/how-do-i-create-a-screenshot-to-illustrate-a-post), a combo. of alt+printscreen on Windows will capture **only** the active window area. – Andrew Thompson Aug 23 '12 at 00:12
  • Possible duplicate of [Show an animated BG in Swing](http://stackoverflow.com/questions/10836832/show-an-animated-bg-in-swing) (Yes Virginia, it will also work for static backgrounds). – Andrew Thompson Aug 23 '12 at 00:17
  • **Cancel that thought** I was too hasty in my assessment. This question is **not** a duplicate of the linked question. Sorry about that. :P – Andrew Thompson Aug 23 '12 at 00:24

1 Answers1

6

Were you copy/pasting code at random? That is what it looked like. There were so many odd aspects to that code that I did not document them all (a good one for code review, maybe). The example uses an asynchronous method to load the images (in order to get the animated image, animating). Use ImageIO.read(URL) for a synchronous way to load static images.

Here are some brief tips:

  1. By the time this becomes deployed, the images will likely become an embedded resource and will not be accessible by File object. Add them to the run-time class-path and access them by URL.
  2. Swing GUIs should be started and altered on the EDT (see the change to the main()).
  3. Always call super.paint(g); (or paintComponent(g)) at the start of the method.
  4. Don't extend frame, don't paint to a top level component. Instead extend panel and override paintComponent(). Add the panel to the frame.

Code

import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import javax.swing.*;

public class JavaGame extends JPanel {

    int x, y;
    private Image dbImage;
    private Graphics dbg;
    Image ghost;
    Image bg;

    public class AL extends KeyAdapter {
        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();

            if (keyCode == e.VK_LEFT) {
                if (x <= 8)
                    x = 8;
                else
                    x += -5;
            }
            if (keyCode == e.VK_RIGHT) {
                if (x >= 235)
                    x = 235;
                else
                    x += +5;
            }
            if (keyCode == e.VK_UP) {
                if (y <= 18)
                    y = 18;
                else
                    y += -5;
            }
            if (keyCode == e.VK_DOWN) {
                if (y >= 235)
                    y = 235;
                else
                    y += +5;
            }
        }

        public void keyReleased(KeyEvent e) {
        }
    }

    public JavaGame() throws Exception {
        // Load images
        //ImageIcon i = new ImageIcon(
            //  "C:/Users/Taylor/workspace/Java game/src/ghost.png");
        URL urlGhost = new URL("http://1point1c.org/gif/thum/plnttm.gif");
        ghost = Toolkit.getDefaultToolkit().createImage(urlGhost);

        //ImageIcon j = new ImageIcon(
            //  "C:/Users/Taylor/workspace/Java game/src/bg.png");
        URL urlBG = new URL("http://pscode.org/media/stromlo2.jpg");
        bg = Toolkit.getDefaultToolkit().createImage(urlBG);
        
        setFocusable(true);

        // Game properties
        addKeyListener(new AL());
        x = 150;
        y = 150;
        
        ActionListener al = new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                repaint();
            }
        };
        Timer timer = new Timer(50,al);
        timer.start();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(bg, 0, 0, null);

        //dbImage = createImage(getWidth(), getHeight());
        //dbg = dbImage.getGraphics();
        //paintComponent(dbg);
        g.drawImage(dbImage, x, y, this);

        g.setColor(Color.WHITE);
        g.drawImage(ghost, x, y, this);

        //repaint();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    JFrame f = new JFrame("Java Game");
                    f.setSize(500, 500);
                    f.setResizable(false);
                    f.setVisible(true);
                    f.setBackground(Color.GRAY);
                    f.setContentPane(new JavaGame());
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
Community
  • 1
  • 1
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • I think you meant to comment out `g.drawImage(dbImage, ...)`. – Jeffrey Aug 23 '12 at 00:56
  • @Jeffrey Actually I completely missed that besides being redundant that image was `null` in my version of the code. I would have commented further on it but I was mystified as to why it was ever there! But you did remind me to mention that.. `g.drawImage(bg, 0, 0, null);` should be `g.drawImage(bg, 0, 0, this);` -- Was there something else? It is easy to lose track in code that bad. – Andrew Thompson Aug 23 '12 at 01:10
  • Pretty much everything I was going to post - beat me to it +1 – Nick Rippe Aug 23 '12 at 01:21
  • @AndrewThompson The OP was probably trying to implement double buffering. Doesn't look like there's anything else, but it's hard to be 100% certain since I didn't test the code. – Jeffrey Aug 23 '12 at 01:26
  • Double buffering was the intention; this code was adhering to the tutorial, which was meant for beginners... that may explain its substandard qualities. – tadamson Aug 23 '12 at 02:38
  • 2
    Being 'for beginners' is no excuse for the tutorial that produced that code. Was it from RoseIndia? But wherever you found it, put it back there and go through the equivalent parts of the Oracle Java Tutorial. If there is anything in those you do not understand, people will be willing to help. – Andrew Thompson Aug 23 '12 at 02:45