Java 我试图在applet中使用线程移动球,但它不移动

Java 我试图在applet中使用线程移动球,但它不移动,java,multithreading,swing,awt,event-dispatch-thread,Java,Multithreading,Swing,Awt,Event Dispatch Thread,我试图用线程在小程序中移动球,但它不移动。有人能帮我吗?我是小程序新手,正在进行游戏开发。这里是我的代码供参考 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=ne

我试图用线程在小程序中移动球,但它不移动。有人能帮我吗?我是小程序新手,正在进行游戏开发。这里是我的代码供参考

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()
}

无法在paint()内调用repaint()方法。而且您不能在paint()方法中组织无限循环-这样做,您将阻止小程序中的绘图。

paint
中有无限循环意味着该方法的任何一个过程都无法完成

另外,在
paint
方法中,永远不要调用
Thread.sleep(100)
。这会阻止EDT,并降低性能


相反,请使用来执行更新和重新绘制工作。此外,我将子类a
JComponent
并重写
paintComponent

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

更新:

在start方法中使用此选项

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

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
不需要
implementrunnable
部分。因为您创建的线程将提供它。

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

任何阻止EDT的代码都会阻止它(除其他外)重新绘制UI并响应用户的输入

您担心paint code永远不会更新屏幕,事实上它会使您的应用程序看起来“挂起”,因为
paint
方法不允许完成,并且正在阻止ETD

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

一般来说,
线程
可能有点杀伤力过大,类似于
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);

        }

    }

}

公共类动画船{
公共静态void main(字符串[]args){
新的动画船();
}
公共救生艇(){
invokeLater(新的Runnable(){
@凌驾
公开募捐{
试一试{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}catch(ClassNotFoundException |实例化Exception | IllegalacessException |不支持ookandfeelException ex){
}
JFrame=新JFrame(“测试”);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(新的BorderLayout());
frame.add(新的AnimationPane());
frame.pack();
frame.setLocationRelativeTo(空);
frame.setVisible(true);
}
});
}
公共类AnimationPane扩展了JPanel{
私人缓冲成像船;
私有int xPos=0;
私有int方向=1;
公共动画窗格(){
试一试{
boat=ImageIO.read(新文件(“boat.png”);
计时器计时器=新计时器(40,新ActionListener(){
@凌驾
已执行的公共无效操作(操作事件e){
xPos+=方向;
如果(xPos+boat.getWidth()>getWidth()){
xPos=getWidth()-boat.getWidth();
方向*=-1;
}否则如果(xPos<0){
xPos=0;
方向*=-1;
}
重新油漆();
}
});
timer.setRepeats(真);
timer.setCoalesce(真);
timer.start();
}捕获(IOEX异常){
例如printStackTrace();
}
}
@凌驾
公共维度getPreferredSize(){
返回boat==null?super.getPreferredSize():新维度(boat.getWidth()*4,boat.getHeight());
}
@凌驾
受保护组件(图形g){
超级组件(g);
int y=getHeight()-boat.getHeight();
g、 drawImage(船、XPO、y、此);
}
}
}
作为旁注。您应该很少需要重写顶级容器(如
JApplet
JFrame
)的
paint
方法,尽管有很多很好的理由,但您最感兴趣的是它们不是双缓冲,这意味着当屏幕更新时,您可能会看到闪烁

最好使用类似于
JPanel
的方法,并替代它的
paintComponent
方法

看看

更多信息


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

我试着把它放在run()里面,但球还是不动。Kay Reimeus…我会尝试使用swing定时器,谢谢。实际上我已经写了run()并且已经写了