java如何实现多个小球碰撞后反弹以及绘制动态图。

本次小实验,通过三个类的编写来实现。
首先创建一个点类,其中包含对点的构造方法和多个需要点的构造方法。
/**
 * 点类,表示平面中的点或始点为原点的向量
 * @author xxx
 *
 */
public class Point {
    
    
    public double x=0.0;  //x坐标或x分量
    public double y=0.0;  //y坐标或y分量
    
    /**
     * 构造方法
     * @param x x坐标或x分量
     * @param y y坐标或y分量
     */
    public Point(double x, double y) {
    
    
        this.x=x;
        this.y=y;
    }
    
    /**
     * 计算两点之间的距离
     * @param p0 点p0
     * @param p1 点p1
     * @return p0、p1之间的距离
     */
    public static double distance(Point p0, Point p1) {
    
    
        double d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2));
        return d;
    }
    
    /**
     * 计算过p0、p1两点的直线与x轴的夹角
     * @param p0 点p0
     * @param p1 点p1
     * @return 过p0、p1两点的直线与x轴的夹角(弧度)
     */
    public static double angle(Point p0, Point p1) {
    
    
        return Math.atan2(p1.y-p0.y, p1.x-p0.x);
    }
    
    /**
     * 坐标系原点平移到p0,计算p1在新坐标系中的坐标
     * @param p0 原点移动到的位置
     * @param p1 需计算坐标的点
     * @return p1在新坐标系中的坐标
     */
    public static Point translation(Point p0, Point p1) {
    
    
        return new Point(p1.x-p0.x, p1.y-p0.y);
    }
    
    /**
     * 坐标系绕原点旋转theta角度,计算p点的新坐标
     * @param p 点p
     * @param theta 坐标系旋转的角度(弧度)
     * @return p点在新坐标系中的坐标
     */
    public static Point rotate(Point p, double theta) {
    
    
        double x1=p.x*Math.cos(theta)+p.y*Math.sin(theta);
        double y1=p.y*Math.cos(theta)-p.x*Math.sin(theta);
        return new Point(x1, y1);
    }
}

其次,编写关于小球的类,其中包含小球的各个属性,以及边界碰撞检测,小球相互碰撞检测,碰撞后小球反弹等构造方法,以及绘制小球的类方法。

import java.awt.Color;
import java.awt.Graphics;

/**
 * 小球类,以圆表示平面中移动的小球
 *
 * @author xxx
 *
 */
public class Ball {
    
    
    Point c; // 圆心坐标
    double radius = 50; // 半径
    Point v; // 速度向量
    Color color ; // 颜色
    
    
    /**
     * 构造方法
     *
     * @param cx    圆心x坐标
     * @param cy    圆心y坐标
     * @param vx    x方向速度分量
     * @param vy    y方向速度分量
     * @param color 小球颜色
     */
    public Ball(double cx, double cy, double vx, double vy, Color color) {
    
    
        this.c = new Point(cx, cy);
        this.v = new Point(vx, vy);
        this.color = color;
    }
    
    /**
     * 移动小球
     */
    public void move() {
    
    
        c.x += v.x;
        c.y += v.y;
    }
    
    /**
     * 边界检测,如超出或碰到边界,则修改小球的坐标及运动方向 左上角为坐标原点,需确保小球处于左上角坐标(0,0),宽width,高height的矩形内
     *
     * @param width  矩形的宽
     * @param height 矩形的高
     */
    public void checkBorder(double width, double height) {
    
    
        // 撞到左边
        if (c.x - radius <= 0) {
    
    
            c.x = radius;
            v.x = -v.x;
        }
        
        // 撞到右边
        if (c.x + radius >= width) {
    
    
            c.x = width - radius;
            v.x = -v.x;
        }
        
        // 撞到上边
        if (c.y - radius <= 0) {
    
    
            c.y = radius;
            v.y = -v.y;
        }
        
        // 撞到下边
        if (c.y + radius >= height) {
    
    
            c.y = height - radius;
            v.y = -v.y;
        }
    }
    
    /**
     * 检测两球是否碰撞
     *
     * @param b1 小球1
     * @param b2 小球2
     * @return 相撞返回true,否则返回false
     */
    public static boolean checkCollision(Ball b1, Ball b2) {
    
    
//        //编写如何判断两个小球的距离是否为0,即可看是否碰撞。
//        if(Point.distance(b1.c,b2.c)>0){
    
    
//            return false;
//        }
//        //若距离d<0,返回false,d>0,则返回true。
//        else {
    
    
//        return true;}
        double r=b2.radius;
        if (Point.distance(b1.c,b2.c)<=2*r){
    
    
            return true;
        }else {
    
    
            return false;
        }
    }
    /**
     * 改变相撞的两球的速度向量
     *
     * @param b1 小球1
     * @param b2 小球2
     */
    public static void change(Ball b1, Ball b2) {
    
    

        // 计算两球中心连线的角度
        double theta = Point.angle(b1.c,b2.c);
        
        // 坐标系绕原点旋转theta角度,计算两球新的速度向量
        b1.v = Point.rotate(b1.v,theta);
        b2.v = Point.rotate(b2.v,theta);
        //Point.translation(b1.c,b2.c)
        
        // 交换两球沿中心连线方向的速度
        double vxt ;
        vxt = b1.v.x;
        b1.v.x = b2.v.x;
        b2.v.x = vxt;
        
        // 将坐标系旋转回来,计算两球在原坐标系中的速度向量
        //Point.translation(b1.c,b2.c)
        b1.v = Point.rotate(b1.v,-theta);
        b2.v = Point.rotate(b2.v,-theta);
    }
    
    /**
     * 绘制小球
     *
     * @param g Graphics对象
     */
    public void draw(Graphics g) {
    
    
        
            g.setColor(color); // 设置画笔颜色
            int x = (int) (c.x - radius); // 外接矩形左上角x座标
            int y = (int) (c.y - radius); // 外接矩形左上角y座标
            int width = (int) radius * 2; // 外接矩形的宽度
            int height = (int) radius * 2; // 外接矩形的高度
            g.fillOval(x, y, width, height); // 给出椭圆的左上角坐标及宽高,绘制填充椭圆
        }
    }

最后,建立测试的主函数方法,定义面板和主窗口,在窗口中绘图,利用计时器实现小球的动态变换即可。

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.util.Timer;
import java.util.TimerTask;

@SuppressWarnings("serial")
class MainPanel extends JPanel {
    
     // 定义面板类
    Ball[] balls; // 小球数组
    
    public MainPanel() {
    
    
        balls = new Ball[5]; // 五个小球
        balls[0] = new Ball(10, 10, 2, 5, Color.YELLOW);
        balls[1] = new Ball(120, 100, 3, 2, Color.BLUE);
        balls[2] = new Ball(150, 50, 4, 1, Color.RED);
        balls[3] = new Ball(250, 0, 5, 3,Color.pink);
        balls[4] = new Ball(300, 200, 6, 4,Color.green);
    }
    
    @Override
    public void paintComponent(Graphics g) {
    
     // 在paint方法中绘图
        super.paintComponent(g);
        
        // 边界和碰撞检测
        for (Ball b1 : balls) {
    
    
            b1.checkBorder(this.getWidth(), this.getHeight()); // 边界检测
            
            for (Ball b2 : balls) {
    
     // 碰撞检测
                if (b1 != b2 && Ball.checkCollision(b1, b2)) {
    
    
                    Ball.change(b1, b2); // 改变两球的速度
                    b1.move(); // 将两球移开一点距离
                    b2.move();
                }
            }
        }
        
        // 移动并绘制小球
        for (Ball b : balls) {
    
    
            b.move(); // 移动小球
            b.draw(g); // 绘制小球
        }
    }
}

@SuppressWarnings("serial")
class GameWindow extends JFrame {
    
     // 主窗口类
    MainPanel panel; // 面板
    Timer timer; // 计时器
    TimerTask task; // 任务
    
    public GameWindow() {
    
    
        super("测试窗口");
        panel = new MainPanel(); // 创建面板对象
        this.add(panel); // 将面板加到窗口中
        
        task = new TimerTask() {
    
     // 创建任务对象
            @Override
            public void run() {
    
     // 执行任务要做的事情在此方法中编写
                panel.repaint(); // 重绘面板刷新
            }
        };
        
        timer = new Timer(); // 创建计时器
        timer.schedule(task, 0, 10); // 延迟0秒后每隔10毫秒执行一次任务
        
        setBounds(200, 100, 800, 600); // 设置窗口的位置及大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口的关闭操作
        setVisible(true); // 使窗口可见
    }
    
}

public class Test {
    
    
    public static void main(String[] args) {
    
    
        new GameWindow(); // 创建窗口对象,显示窗口
    }
}

【这是本人在校第一次写CSDN,之后还会分享很多在校期间学习到的小实验,仅供参考,若哪不对欢迎指正。最后强调,可免费使用代码,但编写不易,点个关注和小心心再走呗!】

猜你喜欢

转载自blog.csdn.net/m0_54200555/article/details/127156016