本次小实验,通过三个类的编写来实现。
首先创建一个点类,其中包含对点的构造方法和多个需要点的构造方法。
/**
* 点类,表示平面中的点或始点为原点的向量
* @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,之后还会分享很多在校期间学习到的小实验,仅供参考,若哪不对欢迎指正。最后强调,可免费使用代码,但编写不易,点个关注和小心心再走呗!】