Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
533 views
in Technique[技术] by (71.8m points)

swing - Java 2D Game: repaint(); makes window grey

I'm trying to make a 2D game in Java, but when I call the repaint() method in a thread there's an odd grey-only window.

Here's the source code I have so far:

  1. Spaceshooter.java

    package spaceshooter;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    public class Spaceshooter extends JFrame implements KeyListener, Runnable {
    
    private Player player = new Player(5, 186, this);
    private boolean up, down;
    
    public Spaceshooter(String title) {
        super(title);
        this.setFocusable(true);
        this.addKeyListener(this);
    }
    
    @Override
    public void paint(Graphics gr) {
        super.paint(gr);
    
        gr.setColor(Color.BLACK);
        gr.fillRect(0, 0, 800, 500);
    
        player.paintPlayer(gr);
    }
    
    public static void main(String[] args) {
        Spaceshooter shooter = new Spaceshooter("Spaceshooter");
        new Thread(shooter).start();
        shooter.setSize(800,500);
        shooter.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        shooter.setVisible(true);
    }
    
    @Override
    public void keyTyped(KeyEvent e) {
    }
    
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == 38) {
            up = true;
            down = false;
        } else if (e.getKeyCode() == 40) {
            down = true;
            up = false;
        }
    }
    
    @Override
    public void keyReleased(KeyEvent e) {
        down = false;
        up = false;
    }
    
    @Override
    public void run() {
        while(true) {
            if (up) {
                player.moveUp();
            } else if (down) {
                player.moveDown();
            }
            repaint();
        try {
            Thread.sleep(20);
        } catch (InterruptedException ex) {
            Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
        }
        }
    }
    }
    
  2. Player.java

     package spaceshooter;
    
     import java.awt.Component;
     import java.awt.Graphics;
     import java.awt.Toolkit;
    
     public class Player {
    
    private int x, y;
    private Component comp;
    
    public Player(int x, int y, Component comp) {
        this.x = x;
        this.y = y;
        this.comp = comp;
    }
    
    public void moveUp() {
        y -= 5;
    }
    
    public void moveDown() {
        y += 5;
    }
    
    public void paintPlayer(Graphics gr) {
        gr.drawImage(Toolkit.getDefaultToolkit().getImage("images/player.png"), x, y, comp);
    }
    
    }
    

Thanks for your answers in advance!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

What is EDT ?

Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe". All GUI related task, any update should be made to GUI while painting process must happen on the EDT, which involves wrapping the request in an event and processing it onto the EventQueue. Then the event are dispatched from the same queue in the one by one in order they en-queued, FIRST IN FIRST OUT. That is, if That is, if Event A is enqueued to the EventQueue before Event B then event B will not be dispatched before event A.

Any task you perform which may take a while, likely to block the EDT, no dispatching will happen, no update will be made and hence your application FREEZES. You will have to kill it to get rid of this freezing state.

In your program, aside from creating your JFrame and making it to visible from the main thread (which we also should not do):

    while(true) {
        if (up) {
            player.moveUp();
        } else if (down) {
            player.moveDown();
        }
        repaint();
    try {
        Thread.sleep(20);
    } catch (InterruptedException ex) {
        Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
    }
    }

you are posting repaint() request from the thread which you sent to sleep right immediately after that.

SwingUtilities.invokeLater():

Swing provides a nice function SwingUtilities.invokeLater(new Runnable(){}) for posting repaint request to the EDT. All you have to do is to write:

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

Now some more thing to mention:

  1. We should not implements a GUI component with Runnable. Make another class implementing Runnable, make your computation inside that then use SwingUtilities to post the component update request.
  2. we should not made custom painting on JFrame directly. JFrame is a top level component. It is more like a container which contains your whole app. If you want custom painting use a custom component MyCanvas extends JComponent.
  3. we should not override paint() function. Instead paintComponent(g) will serve our custom painting purposes nicely.
  4. Learn to use Swing Timer class for timely repeated GUI rendering task.

Tutorial Resource and References:

  1. The Event Dispatch Thread
  2. EventQueue
  3. How to Use Swing Timers
  4. Lesson: Performing Custom Painting

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...