Java案例——小球的弹性碰撞
学习使用AWT时编写的一个模拟弹性碰撞的程序。
package Package1;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
public class BallsTest {
//创建窗口对象
public Frame frame = new Frame("这是一个窗口容器Frame");
//设置桌面(画布)宽度和高度
public int TABLE_WIDTH = 500;
public int TABLE_HEIGHT = 300;
//设置球拍的宽度和高度
public final int RACKET_WIDTH = 80;
public final int RACKET_HEIGHT = 20;
//定义变量,记录球拍的坐标
public int racketX = 120;
public final int racketY = 280;
//设置小球颜色
public Color[] Allcolor = {Color.BLUE, Color.GREEN, Color.CYAN, Color.BLACK, Color.MAGENTA};
//是否结束游戏
volatile boolean flag = false;
//小球个数
public volatile int num = 2;
//设置重力加速度
public int gravity = 1;
//设置刷新周期
public int delay = 30;
private class Ball {
//设置小球的半径
public volatile int ballRadius;
//定义变量,记录小球的坐标
public volatile double ballX;
public volatile double ballY;
//定义变量,记录小球在x和y方向上分别移动的速度
public volatile double speedX;
public volatile double speedY;
//设置小球颜色
public Color color;
Random random = new Random();
//初始化时随机生成坐标速度等信息
public Ball() {
int SPEEDMAX = 20;
int R_MIN = 20;
int R_MAX = 25;
this.ballRadius = random.nextInt(R_MAX - R_MIN) + R_MIN;
this.ballX = random.nextInt(TABLE_WIDTH - 2 * this.ballRadius);
this.ballY = random.nextInt(TABLE_HEIGHT / 2);
this.speedX = random.nextInt(SPEEDMAX * 2) - SPEEDMAX + 5;
this.speedY = 0;
this.color = Allcolor[random.nextInt(Allcolor.length)];
}
public Ball(int ballX, int ballY, int speedX, int speedY, int ballRadius) {
this.ballX = ballX;
this.ballY = ballY;
this.speedX = speedX;
this.speedY = speedY;
this.ballRadius = ballRadius;
}
public int getBallRadius() {
return ballRadius;
}
public void setBallRadius(int BallRadius) {
this.ballRadius = BallRadius;
}
public double getBallX() {
return ballX;
}
public void setBallX(int ballX) {
this.ballX = ballX;
}
public double getBallY() {
return ballY;
}
public void setBallY(int ballY) {
this.ballY = ballY;
}
public double getSpeedX() {
return speedX;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public double getSpeedY() {
return speedY;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
}
ArrayList<Ball> balls = new ArrayList<Ball>(); //创建一些小球
//给小球组设置运动线程,并检测碰撞来修改它们的速度
private class BallMovement implements Runnable {
//每个线程负责跟踪一个小球
private int ballId; //跟踪的小球索引
public BallMovement(int ballId) {
this.ballId = ballId;
}
private boolean isTouched(int ballId, int ballIdOther) {
//碰撞检测
Ball ball0 = balls.get(ballId);
Ball ball1 = balls.get(ballIdOther);
//获取本小球和其它小球的半径和速度、位置
int ball0Radius = ball0.getBallRadius();
double ball0X = ball0.getBallX();
double ball0Y = ball0.getBallY();
double x0 = ball0X + ball0Radius;
double y0 = ball0Y + ball0Radius;
int ball1Radius = ball1.getBallRadius();
double ball1X = ball1.getBallX();
double ball1Y = ball1.getBallY();
double x1 = ball1X + ball1Radius;
double y1 = ball1Y + ball1Radius;
//根据是否发生碰撞改变各自小球类的内部变量值
double deltaX = x1 - x0;
double deltaY = y1 - y0;
double distance = Math.sqrt((Math.pow(deltaX, 2) + Math.pow(deltaY, 2)));
//如果发生碰撞
boolean b = false;
double d = ball0Radius + ball1Radius;
if (distance <= d) {
// double d = ball0.ballRadius + ball1.getBallRadius();
// double x0 = ball0.ballX + ball0.ballRadius;
// double y0 = ball0.ballY + ball0.ballRadius;
// double x1 = ball1.ballX + ball1.ballRadius;
// double y1 = ball1.ballY + ball1.ballRadius;
// double deltaX = x1 - x0;
// double deltaY = y1 - y0;
//---------------------------------
// double theta = Math.atan(deltaY/deltaX);
// //修正一下两个小球的位置
// theta = Math.abs(Math.atan(deltaY/deltaX));
// double dx = d*Math.cos(theta);
// double dy = d*Math.sin(theta);
// if(deltaY >= 0 && deltaX >= 0) {
// ball0.ballX = x1 - dx - ball0.ballRadius;
// ball0.ballY = y1 - dy - ball0.ballRadius;
// }
// if(deltaY < 0 && deltaX >= 0) {
// ball0.ballX = x1 - dx - ball0.ballRadius;
// ball0.ballY = y1 + dy - ball0.ballRadius;
// }
// if(deltaY >= 0 && deltaX < 0) {
// ball0.ballX = x1 + dx - ball0.ballRadius;
// ball0.ballY = y1 - dy - ball0.ballRadius;
// }
// if(deltaY < 0 && deltaX < 0) {
// ball0.ballX = x1 + dx - ball0.ballRadius;
// ball0.ballY = y1 + dy - ball0.ballRadius;
// }
b = true;
}
return b;
}
@Override
public void run() {
double v0X;
double v0Y;
double l0;
double victor01X;
double victor01Y;
double theta0;
double vicTheta0;
double vTheta0;
double v1X;
double v1Y;
double l1;
double victor10X;
double victor10Y;
double theta1;
double vicTheta1;
double vTheta1;
double vicLength;
double tempTheta1;
double tempTheta2;
while (!flag) {
try {
Thread.sleep(delay); //设置刷新时间
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新该小球下一时间点的位置
balls.get(ballId).speedY += gravity;
balls.get(ballId).ballX += balls.get(ballId).speedX;
balls.get(ballId).ballY += balls.get(ballId).speedY;
if (balls.get(ballId).ballX < 0) {
balls.get(ballId).ballX = 0;
balls.get(ballId).speedX = -balls.get(ballId).speedX; //反向运动
} else if (balls.get(ballId).ballX > (TABLE_WIDTH - balls.get(ballId).ballRadius * 2)) {
balls.get(ballId).ballX = (TABLE_WIDTH - balls.get(ballId).ballRadius * 2);
balls.get(ballId).speedX = -balls.get(ballId).speedX; //反向运动
}
if (balls.get(ballId).ballY < 0) {
balls.get(ballId).ballY = 0;
balls.get(ballId).speedY = -balls.get(ballId).speedY; //反向运动
} else if (balls.get(ballId).ballY > (TABLE_HEIGHT - balls.get(ballId).ballRadius * 2)) {
balls.get(ballId).ballY = (TABLE_HEIGHT - balls.get(ballId).ballRadius * 2);
balls.get(ballId).speedY = -balls.get(ballId).speedY; //反向运动
}
//更新该小球的速度属性
for (int j = 0; j < balls.size(); j++) {
if (j > ballId && isTouched(ballId, j)) { //与所有其它小球进行碰撞检测
Ball ball0 = balls.get(ballId);
Ball ball1 = balls.get(j);
victor01X = ball1.getBallX() + ball1.getBallRadius() -
(ball0.getBallX() + ball0.getBallRadius());
victor01Y = ball1.getBallY() + ball1.getBallRadius() -
(ball0.getBallY() + ball0.getBallRadius());
vicTheta0 = Math.atan(victor01Y / victor01X);
vicLength = Math.sqrt(Math.pow(victor01X, 2) + Math.pow(victor01Y, 2));
v0X = (double) ball0.getSpeedX();
v0Y = (double) ball0.getSpeedY();
l0 = Math.sqrt(Math.pow(v0X, 2) + Math.pow(v0Y, 2));
vTheta0 = Math.atan(v0Y / v0X);
theta0 = Math.acos((v0X * victor01X + v0Y * victor01Y) / (l0 * vicLength));
if (vTheta0 > vicTheta0) {
tempTheta1 = vicTheta0 + Math.PI - theta0;
} else {
tempTheta1 = vicTheta0 - Math.PI + theta0;
}
victor10X = -victor01X;
victor10Y = -victor01Y;
vicTheta1 = Math.atan(victor10Y / victor10X);
v1X = (double) ball1.getSpeedX();
v1Y = (double) ball1.getSpeedY();
l1 = Math.sqrt(Math.pow(v1X, 2) + Math.pow(v1Y, 2));
vTheta1 = Math.atan(v1Y / v1X);
theta1 = Math.acos((v1X * victor10X + v1Y * victor10Y) / (l1 * vicLength));
if (vTheta1 > vicTheta1) {
tempTheta2 = vicTheta1 + Math.PI - theta1;
} else {
tempTheta2 = vicTheta1 - Math.PI + theta1;
}
ball0.speedX = (l1 * Math.cos(tempTheta2));
ball0.speedY = (l1 * Math.sin(tempTheta2));
ball1.speedX = (l0 * Math.cos(tempTheta1));
ball1.speedY = (l0 * Math.sin(tempTheta1));
}
}
}
}
}
//自定义一个类继承Canvas充当画布
private class MyCanvas extends Canvas {
public MyCanvas(int TABLE_WIDTH, int TABLE_HEIGHT) {
this.setPreferredSize(new Dimension(TABLE_WIDTH, TABLE_HEIGHT));
}
@Override
public void paint(Graphics g) {
//在这里绘制内容
if (flag) {
//游戏结束
g.setColor(Color.BLUE);
g.setFont(new Font("Times", Font.BOLD, 30));
g.drawString("游戏结束!", 80, 200);
} else {
//游戏中
//绘制小球
for (int i = 0; i < balls.size(); i++) {
g.setColor(balls.get(i).color);
g.fillOval((int) balls.get(i).ballX, (int) balls.get(i).ballY,
balls.get(i).getBallRadius() * 2, balls.get(i).getBallRadius() * 2);
}
//绘制球拍
g.setColor(Color.PINK);
g.fillRect(racketX, racketY, RACKET_WIDTH, RACKET_HEIGHT);
}
}
}
//创建绘画区域(桌面)
MyCanvas myCanvas = new MyCanvas(TABLE_WIDTH, TABLE_HEIGHT);
Timer timer = new Timer(delay, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myCanvas.repaint();
}
});
//组装视图以及游戏逻辑的控制
public void init() {
//添加一系列小球
balls = new ArrayList<Ball>();
for (int i = 0; i < num; i++) {
balls.add(new Ball());
}
//给球拍添加监听器以便控制球拍
MouseMotionListener mouseMotionListener = new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
//获取当前鼠标位置
int mouseX = e.getX();
if (mouseX < (RACKET_WIDTH / 2)) {
//设置球拍的位置
racketX = 0;
} else if (mouseX > (TABLE_WIDTH - RACKET_WIDTH / 2)) {
racketX = TABLE_WIDTH - RACKET_WIDTH;
} else {
racketX = mouseX - (RACKET_WIDTH / 2);
}
}
};
//给frame和myCanvas注册监听器
myCanvas.addMouseMotionListener(mouseMotionListener);
frame.addMouseMotionListener(mouseMotionListener);
frame.add(myCanvas);
//通过pack()方法设置最佳大小
frame.pack();
//设置Frame可见
frame.setVisible(true);
frame.setLocation(500, 200);
//加入线程
timer.start();
for (int i = 0; i < balls.size(); i++) {
new Thread(new BallMovement(i)).start();
}
}
public static void main(String[] args) {
new BallsTest().init();
}
}