Java中多线程程序中弹跳球的图形

发布于 2025-01-23 11:55:05 字数 4566 浏览 1 评论 0原文

我正在尝试调整我为在Java弹跳的程序而创建的程序。如果解决方案是显而易见的,我很抱歉。我使用以下代码创建了一系列弹跳球,然后随后创建了两个螺纹,而球弹跳。

我试图在没有数组的情况下实现这一目标。因此,每个线程只有1个球。 我觉得答案是盯着我的脸,但我无法解决问题。任何帮助将不胜感激。

 import java.awt.Color;
 import java.awt.Graphics;
 import java.util.ArrayList;
 import java.util.Scanner;

 import javax.swing.JFrame;
 import javax.swing.JPanel;



 public class AClass implements Runnable {  
     private JFrame mainFrame;
     private DrawPanel drawPanel;
    // private java.util.List<Ball> balls;


     private int windowWidth = 640;
     private int windowHeight = 480;
     private String windowLabel = "Multi-threaded ball application";

     public void run() {

         //balls = new ArrayList<>();
         //Scanner sc = new Scanner(System.in);
         //System.out.print("Enter the number of balls you would like to create:");
         //int n = sc.nextInt();
         //sc.close();
    
    
         /* Generate balls */
         //for (int i = 0; i < n; i++) {
            Ball ball = new Ball(
                     /* Random positions from 0 to windowWidth or windowHeight */
                     (int) Math.floor(Math.random() * windowWidth),
                     (int) Math.floor(Math.random() * windowHeight),
                     /* Random size from 10 to 30 */
                     (int) Math.floor(Math.random() * 20) + 10,
                     /* Random RGB colors*/
                     new Color(
                             (int) Math.floor((Math.random() * 256)),
                             (int) Math.floor((Math.random() * 256)),
                             (int) Math.floor((Math.random() * 256))
                     ),
                     /* Random velocities from -5 to 5 */
                     (int) Math.floor((Math.random() * 10) - 5),
                     (int) Math.floor((Math.random() * 10) - 5)
             );

            // balls.add(ball);
        // }

         /* Initialize program */
         mainFrame = new JFrame();
         drawPanel = new DrawPanel();
         mainFrame.getContentPane().add(drawPanel);
         mainFrame.setTitle(windowLabel);
         mainFrame.setSize(windowWidth, windowHeight);
         mainFrame.setVisible(true);
         mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

         while (true) {
             //for (Ball b: balls) {
                 ball.update();
            // }

             /* Give Swing 10 milliseconds to see the update! */
             try {
                 Thread.sleep(10);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }

             mainFrame.repaint();
         }
     }

    public class DrawPanel extends JPanel {
         /**
          * 
          */
         private static final long serialVersionUID = 1L;

         @Override
         public void paintComponent(Graphics graphics) {
             super.paintComponent(graphics);

            // for (Ball b: balls) {
                 ball.draw(graphics);
            // }

         }
     }

     class Ball {//ball class
         private int posX, posY, size;
         private Color color;

         private int vx = 5;
         private int vy = 5;

         public Ball(int posX, int posY, int size, Color color, int vx, int vy) {
             this.posX = posX;
             this.posY = posY;
             this.size = size;
             this.color = color;
             this.vx = vx;
             this.vy = vy;
         }

         void update() {

             if (posX > mainFrame.getWidth() || posX < 0) {
                 vx *= -1;
             }

             if (posY > mainFrame.getHeight() || posY < 0) {
                 vy *= -1;
             }

             if (posX > mainFrame.getWidth()) {
                 posX = mainFrame.getWidth();
             }

             if (posX < 0) {
                 posX = 0;
             }

             if (posY > mainFrame.getHeight()) {
                 posY = mainFrame.getHeight();
             }

             if (posY < 0) {
                 posY = 0;
             }

             this.posX += vx;
             this.posY += vy;

         }

         void draw(Graphics g) {
             g.setColor(color);
             g.fillOval(posX, posY, size, size);
         }
     }


     public static void main(String[] args) {  
         AClass ex = new AClass();  
         Thread t1= new Thread(ex);  
         Thread t2 = new Thread(ex);
         t1.start();  
         t2.start();
         //System.out.println("Hi");  
     }  
 }  

I am trying to adapt a program I have created for bouncing a ball in Java. I am very new so apologies if the solution is obvious. I have used the following code to create an array of bouncing balls, and then subsequently create two threads with the balls bouncing.

I am trying to achieve this without the array. So, that each thread has only 1 ball.
I feel like the answer is staring me in the face but I just cannot solve the issue. Any help would be greatly appreciated.

 import java.awt.Color;
 import java.awt.Graphics;
 import java.util.ArrayList;
 import java.util.Scanner;

 import javax.swing.JFrame;
 import javax.swing.JPanel;



 public class AClass implements Runnable {  
     private JFrame mainFrame;
     private DrawPanel drawPanel;
    // private java.util.List<Ball> balls;


     private int windowWidth = 640;
     private int windowHeight = 480;
     private String windowLabel = "Multi-threaded ball application";

     public void run() {

         //balls = new ArrayList<>();
         //Scanner sc = new Scanner(System.in);
         //System.out.print("Enter the number of balls you would like to create:");
         //int n = sc.nextInt();
         //sc.close();
    
    
         /* Generate balls */
         //for (int i = 0; i < n; i++) {
            Ball ball = new Ball(
                     /* Random positions from 0 to windowWidth or windowHeight */
                     (int) Math.floor(Math.random() * windowWidth),
                     (int) Math.floor(Math.random() * windowHeight),
                     /* Random size from 10 to 30 */
                     (int) Math.floor(Math.random() * 20) + 10,
                     /* Random RGB colors*/
                     new Color(
                             (int) Math.floor((Math.random() * 256)),
                             (int) Math.floor((Math.random() * 256)),
                             (int) Math.floor((Math.random() * 256))
                     ),
                     /* Random velocities from -5 to 5 */
                     (int) Math.floor((Math.random() * 10) - 5),
                     (int) Math.floor((Math.random() * 10) - 5)
             );

            // balls.add(ball);
        // }

         /* Initialize program */
         mainFrame = new JFrame();
         drawPanel = new DrawPanel();
         mainFrame.getContentPane().add(drawPanel);
         mainFrame.setTitle(windowLabel);
         mainFrame.setSize(windowWidth, windowHeight);
         mainFrame.setVisible(true);
         mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

         while (true) {
             //for (Ball b: balls) {
                 ball.update();
            // }

             /* Give Swing 10 milliseconds to see the update! */
             try {
                 Thread.sleep(10);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }

             mainFrame.repaint();
         }
     }

    public class DrawPanel extends JPanel {
         /**
          * 
          */
         private static final long serialVersionUID = 1L;

         @Override
         public void paintComponent(Graphics graphics) {
             super.paintComponent(graphics);

            // for (Ball b: balls) {
                 ball.draw(graphics);
            // }

         }
     }

     class Ball {//ball class
         private int posX, posY, size;
         private Color color;

         private int vx = 5;
         private int vy = 5;

         public Ball(int posX, int posY, int size, Color color, int vx, int vy) {
             this.posX = posX;
             this.posY = posY;
             this.size = size;
             this.color = color;
             this.vx = vx;
             this.vy = vy;
         }

         void update() {

             if (posX > mainFrame.getWidth() || posX < 0) {
                 vx *= -1;
             }

             if (posY > mainFrame.getHeight() || posY < 0) {
                 vy *= -1;
             }

             if (posX > mainFrame.getWidth()) {
                 posX = mainFrame.getWidth();
             }

             if (posX < 0) {
                 posX = 0;
             }

             if (posY > mainFrame.getHeight()) {
                 posY = mainFrame.getHeight();
             }

             if (posY < 0) {
                 posY = 0;
             }

             this.posX += vx;
             this.posY += vy;

         }

         void draw(Graphics g) {
             g.setColor(color);
             g.fillOval(posX, posY, size, size);
         }
     }


     public static void main(String[] args) {  
         AClass ex = new AClass();  
         Thread t1= new Thread(ex);  
         Thread t2 = new Thread(ex);
         t1.start();  
         t2.start();
         //System.out.println("Hi");  
     }  
 }  

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

神爱温柔 2025-01-30 11:55:05

首先,秋千不是线程安全。您不应从事件派遣线程的上下文之外更新UI(或UI所依赖的任何状态)。

参见 Swing 同意的同意。

我认为您的意图不是正确的方法(试图让每个球都是自己的线程),您将迅速遇到各种试图进行碰撞检测的问题每个球的状态总是彼此独立,它的缩放效果不佳。数组和秋千计时器将是一个更合适的解决方案。

这可能是我可以接近您想要的东西的问题,问题是,为了绘画,您需要参考ball,所以我扩展了ball 来自JPANEL而不是。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new SurfacePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface Surface {
        public Dimension getSize();

        public void repaint();
    }

    public class SurfacePane extends JPanel implements Surface {
        public SurfacePane() {
            setLayout(null);
            for (int index = 0; index < 10; index++) {
                Ball ball = new Ball(
                        /* Random positions from 0 to windowWidth or windowHeight */
                        (int) Math.floor(Math.random() * 400),
                        (int) Math.floor(Math.random() * 400),
                        /* Random size from 10 to 30 */
                        (int) Math.floor(Math.random() * 20) + 10,
                        /* Random RGB colors*/
                        new Color(
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256))
                        ),
                        /* Random velocities from -5 to 5 */
                        (int) Math.floor((Math.random() * 10) - 5),
                        (int) Math.floor((Math.random() * 10) - 5),
                        this
                );
                add(ball);
                ball.start();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }

    public class Ball extends JPanel {
        private int posX, posY, size;
        private Color color;

        private int vx = 5;
        private int vy = 5;

        private Surface surface;
        private Timer timer;

        public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
            this.posX = posX;
            this.posY = posY;
            this.size = size;
            this.color = color;
            this.vx = vx;
            this.vy = vy;
            this.surface = surface;
            setBackground(color);
            setSize(size, size);
            setOpaque(false);
        }

        public void start() {
            if (timer != null) {
                timer.stop();
            }
            timer = new Timer(10, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    update();
                    surface.repaint();
                }
            });
            timer.start();
        }

        public void stop() {
            if (timer == null) {
                return;
            }
            timer.stop();
        }

        protected void update() {
            int width = surface.getSize().width;
            int height = surface.getSize().height;
            if (posX > width || posX < 0) {
                vx *= -1;
            }

            if (posY > height || posY < 0) {
                vy *= -1;
            }

            if (posX > width) {
                posX = width;
            }

            if (posX < 0) {
                posX = 0;
            }

            if (posY > height) {
                posY = height;
            }

            if (posY < 0) {
                posY = 0;
            }

            this.posX += vx;
            this.posY += vy;

            setLocation(posX, posY);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
            g.setColor(color);
            g.fillOval(0, 0, size, size);
        }
    }
}

这种方法的问题(甚至螺纹 ed方法)是,它的扩展不是很好。例如,在我的实验中,在我开始遇到响应性问题之前,我只有大约5,000个球(调整窗口滞后的大小),而使用arraylist(和大约20,000球)(和大约20,000球)一个单个计时器) - 老实说,帧速率很糟糕,但是UI保持相对响应 - 我可以调整窗口大小,而无需滞后

timer,基于数组>例子...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new SurfacePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface Surface {
        public Dimension getSize();

        public void repaint();
    }

    public class SurfacePane extends JPanel implements Surface {
        private List<Ball> balls = new ArrayList<>(32);

        public SurfacePane() {
            for (int index = 0; index < 20_000; index++) {
                Ball ball = new Ball(
                        /* Random positions from 0 to windowWidth or windowHeight */
                        (int) Math.floor(Math.random() * 400),
                        (int) Math.floor(Math.random() * 400),
                        /* Random size from 10 to 30 */
                        (int) Math.floor(Math.random() * 20) + 10,
                        /* Random RGB colors*/
                        new Color(
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256))
                        ),
                        /* Random velocities from -5 to 5 */
                        (int) Math.floor((Math.random() * 10) - 5),
                        (int) Math.floor((Math.random() * 10) - 5),
                        this
                );
                balls.add(ball);
            }

            Timer timer = new Timer(10, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (Ball ball : balls) {
                        ball.update();
                    }
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 800);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (Ball ball : balls) {
                ball.paint(g2d);
            }
            g2d.dispose();
        }

    }

    public class Ball {
        private int posX, posY, size;
        private Color color;

        private int vx = 5;
        private int vy = 5;

        private Surface surface;
        private Timer timer;

        public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
            this.posX = posX;
            this.posY = posY;
            this.size = size;
            this.color = color;
            this.vx = vx;
            this.vy = vy;
            this.surface = surface;
        }

        protected void update() {
            int width = surface.getSize().width;
            int height = surface.getSize().height;
            if (posX > width || posX < 0) {
                vx *= -1;
            }

            if (posY > height || posY < 0) {
                vy *= -1;
            }

            if (posX > width) {
                posX = width;
            }

            if (posX < 0) {
                posX = 0;
            }

            if (posY > height) {
                posY = height;
            }

            if (posY < 0) {
                posY = 0;
            }

            this.posX += vx;
            this.posY += vy;
        }

        public void paint(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.fillOval(posX, posY, size, size);
        }
    }
}

First, Swing is not thread safe. You should not be updating the UI (or any state the UI relies on) from outside the context of the Event Dispatching Thread.

See Concurrency in Swing for more details.

I don't think your intention is the correct approach (trying to have each ball be it's own Thread), you're going to quickly end up with all sorts of issues trying to do collision detection, as the state of each ball is always changing independent of each other and it won't scale well. The array and a Swing Timer would be a more suitable solution.

This is the probably the closes I can get to what you want, the problem is, in order to paint it, you'd need a reference to the Ball, so I extended Ball from JPanel instead.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new SurfacePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface Surface {
        public Dimension getSize();

        public void repaint();
    }

    public class SurfacePane extends JPanel implements Surface {
        public SurfacePane() {
            setLayout(null);
            for (int index = 0; index < 10; index++) {
                Ball ball = new Ball(
                        /* Random positions from 0 to windowWidth or windowHeight */
                        (int) Math.floor(Math.random() * 400),
                        (int) Math.floor(Math.random() * 400),
                        /* Random size from 10 to 30 */
                        (int) Math.floor(Math.random() * 20) + 10,
                        /* Random RGB colors*/
                        new Color(
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256))
                        ),
                        /* Random velocities from -5 to 5 */
                        (int) Math.floor((Math.random() * 10) - 5),
                        (int) Math.floor((Math.random() * 10) - 5),
                        this
                );
                add(ball);
                ball.start();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }

    public class Ball extends JPanel {
        private int posX, posY, size;
        private Color color;

        private int vx = 5;
        private int vy = 5;

        private Surface surface;
        private Timer timer;

        public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
            this.posX = posX;
            this.posY = posY;
            this.size = size;
            this.color = color;
            this.vx = vx;
            this.vy = vy;
            this.surface = surface;
            setBackground(color);
            setSize(size, size);
            setOpaque(false);
        }

        public void start() {
            if (timer != null) {
                timer.stop();
            }
            timer = new Timer(10, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    update();
                    surface.repaint();
                }
            });
            timer.start();
        }

        public void stop() {
            if (timer == null) {
                return;
            }
            timer.stop();
        }

        protected void update() {
            int width = surface.getSize().width;
            int height = surface.getSize().height;
            if (posX > width || posX < 0) {
                vx *= -1;
            }

            if (posY > height || posY < 0) {
                vy *= -1;
            }

            if (posX > width) {
                posX = width;
            }

            if (posX < 0) {
                posX = 0;
            }

            if (posY > height) {
                posY = height;
            }

            if (posY < 0) {
                posY = 0;
            }

            this.posX += vx;
            this.posY += vy;

            setLocation(posX, posY);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
            g.setColor(color);
            g.fillOval(0, 0, size, size);
        }
    }
}

The problem with this approach (and even the Threaded approach) is, it's not going to scale well. For example, during my experimentation, I only got to about 5, 000 balls before I started have responsiveness issues (resizing the window lagged, alot), compared to roughly 20, 000 balls using an ArrayList (and a single Timer) - I'll be honest, the frame rate was terrible, but the UI remained relatively responsive - I could resize the window without lag

Single Timer, array based example...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new SurfacePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface Surface {
        public Dimension getSize();

        public void repaint();
    }

    public class SurfacePane extends JPanel implements Surface {
        private List<Ball> balls = new ArrayList<>(32);

        public SurfacePane() {
            for (int index = 0; index < 20_000; index++) {
                Ball ball = new Ball(
                        /* Random positions from 0 to windowWidth or windowHeight */
                        (int) Math.floor(Math.random() * 400),
                        (int) Math.floor(Math.random() * 400),
                        /* Random size from 10 to 30 */
                        (int) Math.floor(Math.random() * 20) + 10,
                        /* Random RGB colors*/
                        new Color(
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256)),
                                (int) Math.floor((Math.random() * 256))
                        ),
                        /* Random velocities from -5 to 5 */
                        (int) Math.floor((Math.random() * 10) - 5),
                        (int) Math.floor((Math.random() * 10) - 5),
                        this
                );
                balls.add(ball);
            }

            Timer timer = new Timer(10, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (Ball ball : balls) {
                        ball.update();
                    }
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 800);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (Ball ball : balls) {
                ball.paint(g2d);
            }
            g2d.dispose();
        }

    }

    public class Ball {
        private int posX, posY, size;
        private Color color;

        private int vx = 5;
        private int vy = 5;

        private Surface surface;
        private Timer timer;

        public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
            this.posX = posX;
            this.posY = posY;
            this.size = size;
            this.color = color;
            this.vx = vx;
            this.vy = vy;
            this.surface = surface;
        }

        protected void update() {
            int width = surface.getSize().width;
            int height = surface.getSize().height;
            if (posX > width || posX < 0) {
                vx *= -1;
            }

            if (posY > height || posY < 0) {
                vy *= -1;
            }

            if (posX > width) {
                posX = width;
            }

            if (posX < 0) {
                posX = 0;
            }

            if (posY > height) {
                posY = height;
            }

            if (posY < 0) {
                posY = 0;
            }

            this.posX += vx;
            this.posY += vy;
        }

        public void paint(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.fillOval(posX, posY, size, size);
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文