【问题标题】:Java Bouncing BallJava弹跳球
【发布时间】:2012-10-12 22:20:53
【问题描述】:

我正在尝试编写一个 Java 应用程序,它在屏幕上绘制多个从框架边缘反弹的球。我可以成功抽出一个球。但是,当我添加第二个球时,它会覆盖我绘制的初始球。代码是:

import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;

public class Ball extends JPanel implements Runnable {

    List<Ball> balls = new ArrayList<Ball>();   
Color color;
int diameter;
long delay;
private int x;
private int y;
private int vx;
private int vy;

public Ball(String ballcolor, int xvelocity, int yvelocity) {
    if(ballcolor == "red") {
        color = Color.red;
    }
    else if(ballcolor == "blue") {
        color = Color.blue;
    }
    else if(ballcolor == "black") {
        color = Color.black;
    }
    else if(ballcolor == "cyan") {
        color = Color.cyan;
    }
    else if(ballcolor == "darkGray") {
        color = Color.darkGray;
    }
    else if(ballcolor == "gray") {
        color = Color.gray;
    }
    else if(ballcolor == "green") {
        color = Color.green;
    }
    else if(ballcolor == "yellow") {
        color = Color.yellow;
    }
    else if(ballcolor == "lightGray") {
        color = Color.lightGray;
    }
    else if(ballcolor == "magenta") {
        color = Color.magenta;
    }
    else if(ballcolor == "orange") {
        color = Color.orange;
    }
    else if(ballcolor == "pink") {
        color = Color.pink;
    }
    else if(ballcolor == "white") {     
        color = Color.white;
    }
    diameter = 30;
    delay = 40;
    x = 1;
    y = 1;
    vx = xvelocity;
    vy = yvelocity;
}

protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g.setColor(color);
    g.fillOval(x,y,30,30); //adds color to circle
    g.setColor(Color.black);
    g2.drawOval(x,y,30,30); //draws circle
}

public void run() {
    while(isVisible()) {
        try {
            Thread.sleep(delay);
        } catch(InterruptedException e) {
            System.out.println("interrupted");
        }
        move();
        repaint();
    }
}

public void move() {
    if(x + vx < 0 || x + diameter + vx > getWidth()) {
        vx *= -1;
    }
    if(y + vy < 0 || y + diameter + vy > getHeight()) {
        vy *= -1;
    }
    x += vx;
    y += vy;
}

private void start() {
    while(!isVisible()) {
        try {
            Thread.sleep(25);
        } catch(InterruptedException e) {
            System.exit(1);
        }
    }
    Thread thread = new Thread(this);
    thread.setPriority(Thread.NORM_PRIORITY);
    thread.start();
}

public static void main(String[] args) {
    Ball ball1 = new Ball("red",3,2);
    Ball ball2 = new Ball("blue",6,2);
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add(ball1);
    f.getContentPane().add(ball2);
    f.setSize(400,400);
    f.setLocation(200,200);
    f.setVisible(true);
    new Thread(ball1).start();
    new Thread(ball2).start();
}
}

我想创建一个球列表,然后循环绘制每个球,但我仍然无法将两个球都添加到内容窗格。

感谢您的帮助。

【问题讨论】:

  • 你可以更改Ball的构造函数参数吗?
  • 是的,我只需要画多个弹跳球,这是唯一的要求。
  • 这不能回答你的问题,但我建议将构造函数的参数更改为只接受 Color 对象而不是 String 并且不得不用那个荒谬的 if- 来解析它否则声明。 (即public Ball(Color ballColor, ...) { color = ballColor; ... }
  • 谢谢,很好的建议。将其添加到我的代码中。
  • 参见示例here

标签: java swing jframe


【解决方案1】:

您需要做的是扩充您的 paintComponent 方法。

您需要遍历所有球,然后分别画出一个球,而不是只画一个球。

例子:

protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    for (Ball b: balls) {
        g.setColor(color);
        g.fillOval(x,y,30,30); //adds color to circle
        g.setColor(Color.black);
        g2.drawOval(x,y,30,30); //draws circle
    }
}

【讨论】:

    【解决方案2】:
    package BouncingBallApp.src.copy;
    import java.awt.*;
    
    
    public class Ball {
        private Point location;
        private int radius;
        private Color color;
        private int dx, dy;
        //private Color[] ballArr;
    
        public Ball(Point l, int r, Color c){
            location = l;
            radius = r;
            color = c;
        }
    
        public Ball(Point l, int r){
            location = l;
            radius = r;
            color = Color.RED;
    
        }
    
        public Point getLocation() {
            return location;
        }
    
        public void setLocation(Point location) {
            this.location = location;
        }
    
        public int getRadius() {
            return radius;
        }
    
        public void setRadius(int radius) {
            this.radius = radius;
        }
    
        public Color getColor() {
            return color;
        }
    
        public void setColor(Color color) {
            this.color = color;
    
        }
    
        public void setMotion(int dx, int dy){
            this.dx = dx;
            this.dy = dy;
        }
    
        public void move(){
            location.translate(dx, dy);
        }
    
        public void moveTo(int x, int y){
            location.move(x, y);
        }
    
        public void paint (Graphics g) {
            g.setColor (color);
            g.fillOval (location.x-radius, location.y-radius, 2*radius, 2*radius);
        }
    
        public void reclectHoriz() {
            dy = -dy;       
        }   
    
        public void reclectVert() {
            dx = -dx;       
        }
    }
    
    
    
    
    
    
    
    package BouncingBallApp.src.copy;
    
    public class MyApp {
    
        public static void main(String[] args) {
            MyFrame frm = new MyFrame(10);
            frm.setVisible(true);           
    
            for (int i=0; i<1000; i++){
                frm.stepTheBall();
            }
        }
    }
    
    
    
    
    
    package BouncingBallApp.src.copy;
    
    
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Point;
    import java.util.Random;
    
    import javax.swing.JFrame;
    
    
    public class MyFrame extends JFrame {
        public final int FRAMEWIDTH = 600;
        public final int FRAMEHEIGHT = 400;
    
        private Ball[] ballArr;
    
        private Random random =new Random ();
        private Color[] colors={Color.RED,Color.blue,Color.yellow}; 
        private int ballCnt;
    
        public MyFrame(int ballCnt){
            super();
            setSize(FRAMEWIDTH, FRAMEHEIGHT);
            setTitle("My Bouncing Ball Application");
    
            ballArr = new Ball[ballCnt];
            this.ballCnt = ballCnt;
             int c;
    
            for (int i=0; i < ballCnt; i++){
                int bcn =random.nextInt(colors.length);
                Color ballcolor=colors[bcn];
                ballArr[i] = new Ball(new Point(50,50),c=(int) (Math.random()*10+3)%8,ballcolor);
    
    
                int ddx = (int) (Math.random()*10+2)%8;
                int ddy = (int) (Math.random()*10+2)%8;         
                ballArr[i].setMotion(ddx, ddy); 
                //c++;
            }
        }
    
        public void paint(Graphics g){
            super.paint(g);
            for (int i=0; i < ballCnt; i++){
                ballArr[i].paint(g);    
            }
        }
    
        public void stepTheBall(){
            for (int i=0; i < ballCnt; i++){        
                ballArr[i].move();
    
                Point loc = ballArr[i].getLocation();
    
                if (loc.x < ballArr[i].getRadius() ||
                    loc.x > FRAMEWIDTH-ballArr[i].getRadius()){
                    ballArr[i].reclectVert();
                }
    
                if (loc.y < ballArr[i].getRadius() ||
                        loc.y > FRAMEHEIGHT-ballArr[i].getRadius()){
                    ballArr[i].reclectHoriz();
                }
            }   
            repaint();
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    【讨论】:

    • 没有解释的代码一文不值。
    【解决方案3】:

    用你目前的方法...

    • 我可以看到的主要问题是您将两个不透明的组件放在彼此的顶部...实际上您可能会发现您正在为另一个绕过其中一个...
    • 您应该使用null 布局管理器,否则它将接管并按照您认为合适的方式布局您的球。
    • 您需要确保控制球板的大小和位置。这意味着您已接任布局经理的角色...
    • 您需要随机化小球的速度和位置,以减少它们从同一位置开始并在同一位置移动的机会...
    • 仅在 EDT 的上下文中更新 Ball
    • 您实际上并不需要 X/Y 值,您可以使用面板。

    .

    public class AnimatedBalls {
    
        public static void main(String[] args) {
            new AnimatedBalls();
        }
    
        public AnimatedBalls() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException ex) {
                    } catch (InstantiationException ex) {
                    } catch (IllegalAccessException ex) {
                    } catch (UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new Balls());
                    frame.setSize(400, 400);
                    frame.setVisible(true);
                }
            });
        }
    
        public class Balls extends JPanel {
    
            public Balls() {
                setLayout(null);
                // Randomize the speed and direction...
                add(new Ball("red", 10 - (int) Math.round((Math.random() * 20)), 10 - (int) Math.round((Math.random() * 20))));
                add(new Ball("blue", 10 - (int) Math.round((Math.random() * 20)), 10 - (int) Math.round((Math.random() * 20))));
            }
        }
    
        public class Ball extends JPanel implements Runnable {
    
            Color color;
            int diameter;
            long delay;
            private int vx;
            private int vy;
    
            public Ball(String ballcolor, int xvelocity, int yvelocity) {
                if (ballcolor == "red") {
                    color = Color.red;
                } else if (ballcolor == "blue") {
                    color = Color.blue;
                } else if (ballcolor == "black") {
                    color = Color.black;
                } else if (ballcolor == "cyan") {
                    color = Color.cyan;
                } else if (ballcolor == "darkGray") {
                    color = Color.darkGray;
                } else if (ballcolor == "gray") {
                    color = Color.gray;
                } else if (ballcolor == "green") {
                    color = Color.green;
                } else if (ballcolor == "yellow") {
                    color = Color.yellow;
                } else if (ballcolor == "lightGray") {
                    color = Color.lightGray;
                } else if (ballcolor == "magenta") {
                    color = Color.magenta;
                } else if (ballcolor == "orange") {
                    color = Color.orange;
                } else if (ballcolor == "pink") {
                    color = Color.pink;
                } else if (ballcolor == "white") {
                    color = Color.white;
                }
                diameter = 30;
                delay = 100;
    
                vx = xvelocity;
                vy = yvelocity;
    
                new Thread(this).start();
    
            }
    
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2 = (Graphics2D) g;
    
                int x = getX();
                int y = getY();
    
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g.setColor(color);
                g.fillOval(0, 0, 30, 30); //adds color to circle
                g.setColor(Color.black);
                g2.drawOval(0, 0, 30, 30); //draws circle
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(30, 30);
            }
    
            public void run() {
    
                try {
                    // Randamize the location...
                    SwingUtilities.invokeAndWait(new Runnable() {
                        @Override
                        public void run() {
                            int x = (int) (Math.round(Math.random() * getParent().getWidth()));
                            int y = (int) (Math.round(Math.random() * getParent().getHeight()));
    
                            setLocation(x, y);
                        }
                    });
                } catch (InterruptedException exp) {
                    exp.printStackTrace();
                } catch (InvocationTargetException exp) {
                    exp.printStackTrace();
                }
    
                while (isVisible()) {
                    try {
                        Thread.sleep(delay);
                    } catch (InterruptedException e) {
                        System.out.println("interrupted");
                    }
    
                    try {
                        SwingUtilities.invokeAndWait(new Runnable() {
                            @Override
                            public void run() {
                                move();
                                repaint();
                            }
                        });
                    } catch (InterruptedException exp) {
                        exp.printStackTrace();
                    } catch (InvocationTargetException exp) {
                        exp.printStackTrace();
                    }
                }
            }
    
            public void move() {
    
                int x = getX();
                int y = getY();
    
                if (x + vx < 0 || x + diameter + vx > getParent().getWidth()) {
                    vx *= -1;
                }
                if (y + vy < 0 || y + diameter + vy > getParent().getHeight()) {
                    vy *= -1;
                }
                x += vx;
                y += vy;
    
                // Update the size and location...
                setSize(getPreferredSize());
                setLocation(x, y);
    
            }
        }
    }
    

    这种方法的“主要”问题是每个Ball 都有自己的Thread。当您增加球的数量时,这将很快消耗您的系统资源......

    另一种方法

    从 Hovercraft 开始,您最好为球创建一个容器,其中球不是组件,而是球的“虚拟”概念,包含足够的信息以使它们能够反弹墙壁...

    public class SimpleBalls {
    
        public static void main(String[] args) {
            new SimpleBalls();
        }
    
        public SimpleBalls() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException ex) {
                    } catch (InstantiationException ex) {
                    } catch (IllegalAccessException ex) {
                    } catch (UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Spot");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    Balls balls = new Balls();
                    frame.add(balls);
                    frame.setSize(400, 400);
                    frame.setVisible(true);
    
                    new Thread(new BounceEngine(balls)).start();
    
                }
            });
        }
    
        public static int random(int maxRange) {
            return (int) Math.round((Math.random() * maxRange));
        }
    
        public class Balls extends JPanel {
    
            private List<Ball> ballsUp;
    
            public Balls() {
                ballsUp = new ArrayList<Ball>(25);
    
                for (int index = 0; index < 10 + random(90); index++) {
                    ballsUp.add(new Ball(new Color(random(255), random(255), random(255))));
                }
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                for (Ball ball : ballsUp) {
                    ball.paint(g2d);
                }
                g2d.dispose();
            }
    
            public List<Ball> getBalls() {
                return ballsUp;
            }
        }
    
        public class BounceEngine implements Runnable {
    
            private Balls parent;
    
            public BounceEngine(Balls parent) {
                this.parent = parent;
            }
    
            @Override
            public void run() {
    
                int width = getParent().getWidth();
                int height = getParent().getHeight();
    
                // Randomize the starting position...
                for (Ball ball : getParent().getBalls()) {
                    int x = random(width);
                    int y = random(height);
    
                    Dimension size = ball.getSize();
    
                    if (x + size.width > width) {
                        x = width - size.width;
                    }
                    if (y + size.height > height) {
                        y = height - size.height;
                    }
    
                    ball.setLocation(new Point(x, y));
    
                }
    
                while (getParent().isVisible()) {
    
                    // Repaint the balls pen...
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            getParent().repaint();
                        }
                    });
    
                    // This is a little dangrous, as it's possible
                    // for a repaint to occur while we're updating...
                    for (Ball ball : getParent().getBalls()) {
                        move(ball);
                    }
    
                    // Some small delay...
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ex) {
                    }
    
                }
    
            }
    
            public Balls getParent() {
                return parent;
            }
    
            public void move(Ball ball) {
    
                Point p = ball.getLocation();
                Point speed = ball.getSpeed();
                Dimension size = ball.getSize();
    
                int vx = speed.x;
                int vy = speed.y;
    
                int x = p.x;
                int y = p.y;
    
                if (x + vx < 0 || x + size.width + vx > getParent().getWidth()) {
                    vx *= -1;
                }
                if (y + vy < 0 || y + size.height + vy > getParent().getHeight()) {
                    vy *= -1;
                }
                x += vx;
                y += vy;
    
                ball.setSpeed(new Point(vx, vy));
                ball.setLocation(new Point(x, y));
    
            }
        }
    
        public class Ball {
    
            private Color color;
            private Point location;
            private Dimension size;
            private Point speed;
    
            public Ball(Color color) {
    
                setColor(color);
    
                speed = new Point(10 - random(20), 10 - random(20));
                size = new Dimension(30, 30);
    
            }
    
            public Dimension getSize() {
                return size;
            }
    
            public void setColor(Color color) {
                this.color = color;
            }
    
            public void setLocation(Point location) {
                this.location = location;
            }
    
            public Color getColor() {
                return color;
            }
    
            public Point getLocation() {
                return location;
            }
    
            public Point getSpeed() {
                return speed;
            }
    
            public void setSpeed(Point speed) {
                this.speed = speed;
            }
    
            protected void paint(Graphics2D g2d) {
    
                Point p = getLocation();
                if (p != null) {
                    g2d.setColor(getColor());
                    Dimension size = getSize();
                    g2d.fillOval(p.x, p.y, size.width, size.height);
                }
    
            }
        }
    }
    

    因为这是由单线程驱动的,所以它的可扩展性要高得多。

    您也可以查看the images are not loading,这是一个类似的问题;)

    【讨论】:

    • 嘿,您将如何解决第二个示例中的“这有点危险,因为在我们更新时可能会发生重绘......”?我尝试检查 null 但由于此时的球不再是 null 了,这并没有多大帮助。有什么想法吗?
    • 您有两个选择,要么围绕球列表同步,要么使用 BufferedStrategy 并控制绘画过程
    • @AdamsonJeremiah Goid,因为这里热得惊人……哦,别等那种粉丝?
    • 关于上面 cmets 中讨论的“危险重绘”,我们是否也可以在 PanelBalls 中添加一个布尔标志 'allowRepaint' 并让 move() 在其第一行将其设置为 false 并在最后一行代码中将其设置回 true?
    • @SeekAndDestroy 您必须确保将标志设置得如此之远,以确保维持正确的线程同步(因此您不会以脏读告终)-事实是,您通过使用多个线程获得的收益更少,与使用单个计时器/线程相比,同步、线程锁定和等待的额外开销。因为绘画可以在任何时候发生,你也冒着“冻结”用户界面的风险
    【解决方案4】:

    您需要在这里使用两个完全不同的类——一个用于 BallContainer,它扩展了 JPanel 并且是绘制球的组件,另一个用于不扩展任何东西而是保存球的坐标和颜色的 Ball。 BallContainer 应该拥有一个List&lt;Ball&gt;,它会在移动它们和绘制它们时进行迭代。

    【讨论】:

    • 感谢您的回答,如果我理解正确,我会创建球列表并遍历列表,绘制每个球。我可以调用仍然属于 Ball 类的 paintComponent 函数吗?我对 Java 还很陌生,在单独的类中调用函数时遇到了一些问题。
    • @nomad:同样,如果我要编写此代码,Ball 将没有 paintComponent 方法,因为它不是组件。我会给它一个draw(Graphics g) 方法,然后在 BallContainer 的 paintComponent(...) 方法中遍历我的 Ball List,在循环内调用每个 Ball 的 draw(g) 方法。
    猜你喜欢
    • 2021-06-20
    • 2023-03-13
    • 2013-05-23
    • 2021-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多