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

时间:2013-01-22 10:12:30

标签: java multithreading swing awt event-dispatch-thread

我正在尝试使用线程在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()
}

6 个答案:

答案 0 :(得分:2)

paint中有一个无限循环意味着该方法不能完成一次传递。

此外,您不应该在Thread.sleep(100)方法中致电paint。这会阻止EDT并降低性能。

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

答案 1 :(得分:2)

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

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

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

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

一般来说,Thread可能有点过度杀戮,在这种情况下,像javax.swing.Timer这样的东西会更合适。

enter image description here

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);

        }

    }

}

作为旁注。您应该很少需要覆盖paintJApplet等顶级容器的JFrame方法,而有很多很好的理由,那就是你要去的那个最感兴趣的是它们不是双缓冲的,这意味着当屏幕更新时你可能会看到闪烁。

最好使用JPanel之类的内容,而不是覆盖它的paintComponent方法。

看看

了解更多信息

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

在Runnable的run方法中使用while循环。

<强>更新

在start方法中有这个。

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

您创建一个没有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部分。由于您创建的主题将提供它。