我正在尝试用Java制作2D游戏,但是当我在一个线程中调用repaint()方法时,会出现一个奇怪的灰色窗口。
这是我到目前为止的源代码:
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);
}
}
}
}
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);
}
}
提前感谢您的回答!
答案 0 :(得分:2)
什么是EDT?
Swing事件处理代码在称为事件派发线程的特殊线程上运行。大多数调用Swing方法的代码也在这个线程上运行。这是必要的,因为大多数Swing对象方法都不是“线程安全的”。所有与GUI相关的任务,都应该对GUI进行任何更新,而绘制过程必须在EDT上进行,这涉及将请求包装在事件中并将其处理到EventQueue
。然后,事件将从一个接一个的队列中按顺序分派,即FIRST IN FIRST OUT。也就是说,如果是,如果Event A
在EventQueue
之前排队到Event B
,那么事件B将不会在事件A之前被调度。
您执行的任何可能需要一段时间的任务,可能会阻止EDT,不会发生任何调度,也不会进行更新,因此您的应用程序会冻结。你将不得不杀死它以摆脱这种冰冻状态。
在你的程序中,除了创建你的JFrame
并使其从主线程(我们也不应该这样做)可见之外:
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);
}
}
您正在发送repaint()
请求,该请求会在您之后立即发送给您睡觉的线程。
<强> SwingUtilities.invokeLater():强>
Swing为向EDT发布SwingUtilities.invokeLater(new Runnable(){})
请求提供了一个很好的功能repaint
。你所要做的就是写:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});
现在还要提一下:
Runnable
实现GUI组件。创建另一个实现Runnable
的类,在其中进行计算,然后使用SwingUtilities
发布组件更新请求。 JFrame
上进行自定义绘画。 JFrame是一个顶级组件。它更像是一个包含整个应用程序的容器。如果您想要自定义绘画,请使用自定义组件MyCanvas extends JComponent
。paint()
函数。相反,paintComponent(g)
将很好地服务于我们的自定义绘画目的。 教程资源和参考: