我正在尝试使用线程在applet中移动一个球,但它没有移动

我正在尝试使用线程在applet中移动一个球,但它没有移动。 任何人都可以帮助我作为applet的新手并继续进行游戏开发。这里的参考是我的代码

public class ballGame extends JApplet implements Runnable { int x_pos=50; int y_pos=100; int rad=10; Thread t; public void start() { super.start(); t=new Thread("t"); t.start(); } public void paint(Graphics g) { super.paint(g); g.setColor(Color.red); setBackground(Color.BLACK); g.drawOval(x_pos,y_pos,2*rad,2*rad); while(true) { x_pos++; //validate(); repaint(); try { Thread.sleep(100); } catch(Exception e) { e.printStackTrace(); } }//end of while }//end of paint() } 

Swing是一个单线程环境。 也就是说,所有更新和交互都在单个线程中执行。 Swing也不是线程安全的。 这意味着UI的所有更新必须在该线程(事件调度线程或ETD)的上下文中执行。

任何阻止EDT的代码都会阻止它(重要的是)重新绘制UI并响应用户的输入。

你的油漆代码永远不会更新屏幕,实际上它会使你的应用程序看起来“挂起”,因为不允许paint方法完成并阻止ETD。

paint方法在调用后会快速返回并且可以快速连续重复调用,这是一个例外。

一般来说,一个Thread可能有点过度杀戮,就像javax.swing.Timer在这些情况下更适合。

在此处输入图像描述

 public class AnimatedBoat { public static void main(String[] args) { new AnimatedBoat(); } public AnimatedBoat() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new AnimationPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class AnimationPane extends JPanel { private BufferedImage boat; private int xPos = 0; private int direction = 1; public AnimationPane() { try { boat = ImageIO.read(new File("boat.png")); Timer timer = new Timer(40, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { xPos += direction; if (xPos + boat.getWidth() > getWidth()) { xPos = getWidth() - boat.getWidth(); direction *= -1; } else if (xPos < 0) { xPos = 0; direction *= -1; } repaint(); } }); timer.setRepeats(true); timer.setCoalesce(true); timer.start(); } catch (IOException ex) { ex.printStackTrace(); } } @Override public Dimension getPreferredSize() { return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); int y = getHeight() - boat.getHeight(); g.drawImage(boat, xPos, y, this); } } } 

作为旁注。 你应该很少需要覆盖顶级容器的paint方法,比如JAppletJFrame ,虽然有很多很好的理由,你最感兴趣的是它们不是双重的缓冲,意味着您可能会在屏幕更新时看到闪烁。

最好使用像JPanel这样的东西,然后覆盖它的paintComponent方法。

看一眼

  • 执行自定义绘画
  • Swing中的并发性
  • 在AWT和Swing中绘画

欲获得更多信息

虽然我在我的示例中使用了JFrame ,但是将动画面板放入JApplet是一件简单的事情,这是您不需要/想要从顶级容器扩展的另一个原因;)

paint具有无限循环意味着不能完成该方法的单个传递。

你也不应该在paint方法中调用Thread.sleep(100) 。 这会阻止EDT并降低性能。

而是使用Swing Timer进行更新和重新绘制工作。 我也会对JComponent子类化并覆盖paintComponent

你不能在paint()中调用repaint()方法。 并且你无法在paint()方法中无限循环组织 – 这样做,你就阻止了你的applet中的绘图。

x_pos是一个int值,因此它按值传递给方法,而不是通过引用传递给方法。 这就是为什么当你改变它的值时,你圈内的值不会更新……

您创建一个没有run()方法的线程。 这个方法应该包含可运行的代码…此外, paint()方法是绘制东西,而不是更新东西!

所以将while循环从paint()方法移动到线程的run()方法:

 t=new Thread("t") { @Override public void run() { while(true) { x_pos++; //validate(); repaint(); try { Thread.sleep(100); } catch(Exception e) { e.printStackTrace(); } }//end of while } }; 

请注意, ballGame不需要implement Runnable部分。 由于您创建的线程将提供它。

在Runnable的run方法中有while循环。

更新:

在start方法中有这个。

 t=new Thread(this); t.start();