java反弹球鼠标逃逸 [英] java bounceBall mouse escape

查看:30
本文介绍了java反弹球鼠标逃逸的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有问题.我是 Java 的初学者,到目前为止已经成功.添加随机大小的气泡.现在我需要让气泡在老鼠靠近它们时逃脱.谁能给我一个提示如何?

I have a problem. I am a beginner with java, and succeeded up to this point. Add bubbles with random sizes. Now I need to make the bubbles escaping mouse when he gets near them. Can anyone give me a hint how?

谢谢.

public class BounceBall extends JFrame {

    private ShapePanel drawPanel;
    private Vector<NewBall> Balls;
    private JTextField message;

// set up interface
    public BounceBall() {
        super("MultiThreading");
        drawPanel = new ShapePanel(400, 345);
        message = new JTextField();
        message.setEditable(false);

        Balls = new Vector<NewBall>();
        add(drawPanel, BorderLayout.NORTH);
        add(message, BorderLayout.SOUTH);

        setSize(400, 400);
        setVisible(true);
    }

    public static void main(String args[]) {

        BounceBall application = new BounceBall();
        application.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    private class NewBall extends Thread {

        private Ellipse2D.Double thisBall;
        private boolean ballStarted;
        private int size, speed; // characteristics
        private int deltax, deltay; // of the ball

        public NewBall() {
            ballStarted = true;
            size = 10 + (int) (Math.random() * 60);
            speed = 10 + (int) (Math.random() * 100);
            int startx = (int) (Math.random() * 300);
            int starty = (int) (Math.random() * 300);
            deltax = -10 + (int) (Math.random() * 21);
            deltay = -10 + (int) (Math.random() * 21);
            if ((deltax == 0) && (deltay == 0)) {
                deltax = 1;
            }
            thisBall = new Ellipse2D.Double(startx, starty, size, size);
        }

        public void draw(Graphics2D g2d) {
            if (thisBall != null) {
                g2d.setColor(Color.BLUE);
                g2d.fill(thisBall);
            }
        }

        public void run() {
            while (ballStarted) // Keeps ball moving
            {
                try {
                    Thread.sleep(speed);
                } catch (InterruptedException e) {
                    System.out.println("Woke up prematurely");
                }

                // calculate new position and move ball
                int oldx = (int) thisBall.getX();
                int oldy = (int) thisBall.getY();
                int newx = oldx + deltax;
                if (newx + size > drawPanel.getWidth() || newx < 0) {
                    deltax = -deltax;
                }
                int newy = oldy + deltay;
                if (newy + size > drawPanel.getHeight() || newy < 0) {
                    deltay = -deltay;
                }
                thisBall.setFrame(newx, newy, size, size);
                drawPanel.repaint();
            }
        }
    }

    private class ShapePanel extends JPanel {

        private int prefwid, prefht;

        public ShapePanel(int pwid, int pht) {
            prefwid = pwid;
            prefht = pht;

            // add ball when mouse is clicked
            addMouseListener(
                    new MouseAdapter() {
                        public void mouseClicked(MouseEvent e) {
                            NewBall nextBall = new NewBall();
                            Balls.addElement(nextBall);
                            nextBall.start();
                            message.setText("Number of Balls: " + Balls.size());

                        }
                    });
        }

        public Dimension getPreferredSize() {
            return new Dimension(prefwid, prefht);
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            for (int i = 0; i < Balls.size(); i++) {
                (Balls.elementAt(i)).draw(g2d);
            }
        }
    }
}

推荐答案

你不应该为每个单独的球设置一个Thread,这不会很好地缩放,你添加的球越多,线程就越多你加.在某些时候,管理线程所需的工作量将超过使用多线程的好处......

You should not have a Thread for each individual ball, this will not scale well, the more balls you add, the more threads you add. At some point, the amount of work it takes to manage the threads will exceed the benefit for using multiple threads...

另外,我怀疑您是否需要 1000fps……对于您的简单目的来说,25fps 之类的东西应该绰绰有余.这会给系统一些喘息的空间,让系统内的其他线程有时间执行.

Also, I doubt if your need 1000fps...something like 25fps should be more than sufficient for your simple purposes. This will give the system some breathing room and allow other threads within the system time to execute.

让我们从一个简单的 Ball 概念开始.Ball 知道它在哪里以及它移动的方向,它还知道如何绘制自己,例如......

Lets start with a simple concept of a Ball. The Ball knows where it is and which direction it is moving it, it also knows how to paint itself, for example...

public class Ball {

    private int x;
    private int y;

    private int deltaX;
    private int deltaY;

    private int dimeter;

    private Ellipse2D ball;
    private Color color;

    public Ball(Color color, Dimension bounds) {
        this.color = color;
        Random rnd = new Random();

        dimeter = 5 + rnd.nextInt(15);

        x = rnd.nextInt(bounds.width - dimeter);
        y = rnd.nextInt(bounds.height - dimeter);

        if (x < 0) {
            x = 0;
        }
        if (y < 0) {
            y = 0;
        }

        int maxSpeed = 10;
        do {
            deltaX = rnd.nextInt(maxSpeed) - (maxSpeed / 2);
        } while (deltaX == 0);

        do {
            deltaY = rnd.nextInt(maxSpeed) - (maxSpeed / 2);
        } while (deltaY == 0);

        ball = new Ellipse2D.Float(0, 0, dimeter, dimeter);

    }

    public void update(Dimension bounds) {

        x += deltaX;
        y += deltaY;

        if (x < 0) {
            x = 0;
            deltaX *= -1;
        } else if (x + dimeter > bounds.width) {
            x = bounds.width - dimeter;
            deltaX *= -1;
        }
        if (y < 0) {
            y = 0;
            deltaY *= -1;
        } else if (y + dimeter > bounds.height) {
            y = bounds.height - dimeter;
            deltaY *= -1;
        }

    }

    public void paint(Graphics2D g2d) {

        g2d.translate(x, y);
        g2d.setColor(color);
        g2d.fill(ball);
        g2d.translate(-x, -y);

    }

}

接下来,我们需要让球在某个地方移动,例如某种BallPit...

Next, we need somewhere for the balls to move within, some kind of BallPit for example...

public class BallPit extends JPanel {

    private List<Ball> balls;

    public BallPit() {
        balls = new ArrayList<>(25);
        balls.add(new Ball(Color.RED, getPreferredSize()));

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

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

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        for (Ball ball : balls) {
            ball.paint(g2d);
        }
        g2d.dispose();
    }

}

这会维护一个球列表,告诉它们何时需要更新以及何时需要绘制.这个例子使用了一个简单的 javax.swing.Timer,它充当更新球和安排重绘的中央计时器.

This maintains a list of balls, tells them when the need to update and when the need to paint. This example uses a simple javax.swing.Timer, which acts as the central timer which updates the balls and schedules the repaints.

这样做的原因是要注意更新和油漆之间的同步,这意味着球在被绘制时不会更新.这是因为 javax.swing.Timer 在 EDT 的上下文中触发它的回调.

The reason for this is takes care of synchronisation between the updates and the paints, meaning that the balls won't be updating while they are been painted. This is achieved because javax.swing.Timer triggers it's callbacks within the context of the EDT.

参见 Swing 中的并发如何使用 Swing Timers 了解更多详情.

See Concurrency in Swing and How to use Swing Timers for more details.

好的,这样就解决了线程问题,但是如何避免鼠标...

Okay, so that fixes the threading issues, but what about the mouse avoidance...

这有点复杂...

我们需要在BillPit中添加一个MouseMoitionListener,并记录鼠标的最后位置.

What we need to is add a MouseMoitionListener to the BillPit and record the last position of the mouse.

public class BallPit extends JPanel {
    //...
    private Point mousePoint;
    //...
    public BallPit() {
        //...
        MouseAdapter handler = new MouseAdapter() {

            @Override
            public void mouseMoved(MouseEvent e) {
                mousePoint = e.getPoint();
            }

            @Override
            public void mouseExited(MouseEvent e) {
                mousePoint = null;
            }

        };

        addMouseListener(handler);
        addMouseMotionListener(handler);
        //...

包含 mouseExit 的原因是为了确保球不会试图远离幻影鼠标光标...

The reason for including mouseExit is to ensure that balls don't try and move away from a phantom mouse cursor...

接下来,我们需要更新 Ball 以获得效果区域",这是球周围的区域,如果鼠标光标在其范围内移动,则会触发运动变化..

Next, we need to update Ball to have an "area of effect", this is the area around the ball that will trigger a change in movement if the mouse cursor moves within it's range...

public class Ball {
    //...
    private final Ellipse2D.Float areaOfEffect;

    public Ball(Color color, Dimension bounds) {
        //...
        areaOfEffect = new Ellipse2D.Float(-10, -10, dimeter + 20, dimeter + 20);
    }

现在,出于调试原因,我还添加了一些额外的绘画...

Now, I also add some additional painting for debug reasons...

public void paint(Graphics2D g2d) {

    g2d.translate(x, y);
    g2d.setColor(new Color(0, 0, 192, 32));
    g2d.fill(areaOfEffect);
    g2d.setColor(color);
    g2d.fill(ball);
    g2d.translate(-x, -y);

}

接下来,我们需要修改Ballupdate方法来接受mousePoint值...

Next, we need to modify the Ball's update method to accept the mousePoint value...

public void update(Dimension bounds, Point mousePoint) {

    PathIterator pathIterator = areaOfEffect.getPathIterator(AffineTransform.getTranslateInstance(x, y));
    GeneralPath path = new GeneralPath();
    path.append(pathIterator, true);
    if (mousePoint != null && path.contains(mousePoint)) {

        // Determine which axis is closes to the cursor...
        int xDistance = Math.abs(x + (dimeter / 2) - mousePoint.x);
        int yDistance = Math.abs(y + (dimeter / 2) - mousePoint.y);

        if (xDistance < yDistance) {
            // If x is closer, the change the delatX
            if (x + (dimeter / 2) < mousePoint.x) {
                if (deltaX > 0) {
                    deltaX *= -1;
                }
            } else {
                if (deltaX > 0) {
                    deltaX *= -1;
                }
            }
        } else {
            // If y is closer, the change the deltaY
            if (y + (dimeter / 2) < mousePoint.y) {
                if (deltaY > 0) {
                    deltaY *= -1;
                }
            } else {
                if (deltaY > 0) {
                    deltaY *= -1;
                }
            }
        }

    }
    //...Rest of previous method code...
}

基本上,这试图做的是确定哪个轴更接近鼠标点以及球应该尝试朝哪个方向移动……这有点基本",但给出了基本前提……

Basically, what this is trying to do is determine which axis is closer to the mouse point and in which direction the ball should try and move...it's a little "basic", but gives the basic premise...

最后,我们需要更新javax.swing.Timer中的update"循环以提供附加参数

Lastly, we need to update the "update" loop in the javax.swing.Timer to supply the additional parameter

for (Ball ball : balls) {
    ball.update(getSize(), mousePoint);
}

这篇关于java反弹球鼠标逃逸的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆