弹球游戏是一款模拟多线程运行的游戏,利用继承Thread实现多线程。
效果图:小球之间会相互碰撞并交换速度在弹开。按钮能实现随机添加、移除小球并暂停小球移动。
具体实现:
1、创建界面(包含主函数)。
public class BallFrame extendsJFrame {public static voidmain(String[] args) {
BallFrame bf= newBallFrame();
bf.initUI();
}
public voidinitUI(){
//设置界面属性,包括按钮背景,添加监听
}
}
//创建一个链表存储小球private ArrayList list = new ArrayList();
2、创建小球类,继承Thread类。这个类包含以下内容。
public class Ball extendsThread {
//需要设置的属性有:
private int x, y;// 小球圆心坐标
private Graphics2D g;
Random rand = new Random();
private ArrayList list;//存贮每个小球
int r = 12;//小球半径
int green,red,blue;//小球的颜色
}
(1)、重写构造方法,主要用来传递参数,包括画笔,面板等。
(2)、画一个小球的方法。
public voiddrawNew(Graphics2D g) {//绘制新的小球
for (int i = r; i > 0; i--) {
//设置颜色使小球看起来具有立体感。if(green<235)green=G + 8*(r-i) ;if(red<235)red=R + 8*(r-i) ;if(blue<235)blue=B + 8*(r-i) ;
g.setColor(newColor(green,red,blue));
g.fillOval(x- r / 2 - i / 2, y - r / 2 - i / 2, i * 2, i * 2);
}
}
(3)、小球的移动方法。
public voidmove() {
x+=xspeed;
y= y+yspeed;
}
(4)、擦除小球的方法,画出新的小球之前要把上一个小球擦出,不然画不出小球的动态效果。
public voiddraw(Graphics2D g) {//擦除上一次绘制的小球
g.setColor(Color.black);
g.fillOval(x- r - 1, y - r - 1, r * 2 + 2, r * 2 + 2);
}
(5)、判断小球碰撞的方法,判断这个小球是否与链表内其他的小球存在碰撞,同时也判断是否与边界发生碰撞。
public voidcollide() {int index=list.indexOf(this);for (int i =index; i < list.size(); i++) {
Ball ball=list.get(i);//判断是否发生碰撞
if (ball != this) {//计算两个小球的距离
int d = (int) Math.sqrt((this.x - ball.x) * (this.x -ball.x)+ (this.y - ball.y) * (this.y -ball.y));if (d < (this.r + ball.r+2)) {int m = this.xspeed;this.xspeed =ball.xspeed;
ball.xspeed=m;
m= this.yspeed;this.yspeed =ball.yspeed;
ball.yspeed=m;
}
}
}
//判断是否与边界发生碰撞if (x + r > centerPanel.getWidth() || x
xspeed= -xspeed;if (y + r > centerPanel.getHeight() || y
yspeed= -yspeed;
}
(6)、线程运行的方法。
public voidrun() {while (true) {this.draw(g);this.move();this.collide();this.drawNew(g);try{
//每40ms循环一次模拟小球的动态效果
Thread.sleep(40);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
3、事件的处理(添加、删除、暂停),主要通过鼠标实现。
(1)、添加小球。在随机位置产生一个小球,初始速度也随机。
if (e.getActionCommand().equals("添加")) {
//随机设置小球的初始位置int x = rand.nextInt(centerPanel.getWidth() - 24) + 12;int y = rand.nextInt(centerPanel.getHeight() - 24) + 12;//创建小球对象
Ball ball = new Ball(list, x, y, centerPanel, this);
ball.start();//启动线程
list.add(ball);//将小球存入链表
System.out.println(list.size());
flag= true;
}
(2)、删除小球,随机删除界面中一个小球。
if (e.getActionCommand().equals("移除")) {//随机移除小球
if (list.size() > 0) {int index =rand.nextInt(list.size());
Ball ball=list.remove(index);
ball.draw(g);//清除被移除的小球
ball.stop();
System.out.println(ball);
}elseSystem.out.println("没有可移除的小球!");
}
(3)、暂停小球。
Thread中有可以让线程等待的方法,但是时间不具有灵活性,这里考虑用sleep方法来实现。
在run()方法中插入sleep,手动改变sleep的条件。
public voidrun() {while (true) {this.draw(g);this.move();this.collide();this.drawNew(g);try{
Thread.sleep(40);
}catch(InterruptedException e) {
e.printStackTrace();
}//以下是插入的代码
while(flag){ //flag就是控制暂停的条件
try{
Thread.sleep(1);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
//监听中的方法
if (e.getActionCommand().equals("暂停")) {
if (flag == false) {
flag = true;
} else if (flag == true) {
flag = false;
}
for (int i = 0; i < list.size(); i++) {
Ball ball = list.get(i);
ball.setFlag(flag);
}
}
完整代码:
importjava.awt.BorderLayout;importjava.awt.Color;importjavax.swing.JButton;importjavax.swing.JFrame;importjavax.swing.JPanel;public class BallFrame extendsJFrame {private static final long serialVersionUID = 1L;public static voidmain(String[] args) {//线程
BallFrame bf = newBallFrame();
bf.initUI();
}public voidinitUI(){this.setTitle("弹球");this.setSize(400, 500);this.setDefaultCloseOperation(3);this.setLocationRelativeTo(null);this.setResizable(false);/***********北边************/JPanel northPanel= newJPanel();
northPanel.setBackground(Color.black);
JButton butAdd= new JButton("添加");
JButton butRemove= new JButton("移除");
JButton butPause= new JButton("暂停");
northPanel.add(butAdd);
northPanel.add(butRemove);
northPanel.add(butPause);this.add(northPanel,BorderLayout.NORTH);/***********中间************/JPanel centerPanel= newJPanel();
centerPanel.setBackground(Color.black);this.add(centerPanel,BorderLayout.CENTER);this.setVisible(true);//事件处理类
BallListener bl = newBallListener(centerPanel);//给事件源添加动作监听方法,指定事件处理类的对象bl
butAdd.addActionListener(bl);
butRemove.addActionListener(bl);
butPause.addActionListener(bl);
}
}
importjava.awt.Color;importjava.awt.Graphics2D;importjava.awt.RenderingHints;importjava.util.ArrayList;importjava.util.Random;importjavax.swing.JPanel;/*** 小球类*/
public class Ball extendsThread {private int x, y;//小球圆心坐标
privateGraphics2D g;
Random rand= newRandom();privateJPanel centerPanel;private ArrayListlist;int r = 12;intgreen,red,blue;int G = rand.nextInt(155);int R = rand.nextInt(155);int B = rand.nextInt(155);int xspeed = 3;int yspeed = 3;
BallListener bl;public Ball(ArrayList list, int x, inty, JPanel centerPanel,
BallListener bl) {this.x =x;this.y =y;this.list =list;this.centerPanel =centerPanel;this.g =(Graphics2D) centerPanel.getGraphics();this.bl =bl;//设置取表画笔的锯齿状
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}private boolean flag = true;public void setFlag(booleanflag) {this.flag =flag;
}public booleangetFlag() {returnflag;
}public voiddraw(Graphics2D g) {//擦除上一次绘制的小球
g.setColor(Color.black);
g.fillOval(x- r - 1, y - r - 1, r * 2 + 2, r * 2 + 2);
}public voiddrawNew(Graphics2D g) {//绘制新的小球
for (int i = r; i > 0; i--) {if(green<235)green=G + 8*(r-i) ;if(red<235)red=R + 8*(r-i) ;if(blue<235)blue=B + 8*(r-i) ;
g.setColor(newColor(green,red,blue));
g.fillOval(x- r / 2 - i / 2, y - r / 2 - i / 2, i * 2, i * 2);
}
}public voidmove() {
x+=xspeed;
y= (int)(y+yspeed);
}public voidcollide() {int index=list.indexOf(this);for (int i =index; i < list.size(); i++) {
Ball ball=list.get(i);//判断是否发生碰撞
if (ball != this) {//计算两个小球的距离
int d = (int) Math.sqrt((this.x - ball.x) * (this.x -ball.x)+ (this.y - ball.y) * (this.y -ball.y));if (d < (this.r + ball.r+2)) {int m = this.xspeed;this.xspeed =ball.xspeed;
ball.xspeed=m;
m= this.yspeed;this.yspeed =ball.yspeed;
ball.yspeed=m;
}
}
}if (x + r > centerPanel.getWidth() || x
xspeed= -xspeed;if (y + r > centerPanel.getHeight() || y
yspeed= -yspeed;
}public voidrun() {while (true) {this.draw(g);this.move();this.collide();this.drawNew(g);try{
Thread.sleep(40);
}catch(InterruptedException e) {
e.printStackTrace();
}while(flag^true){try{
Thread.sleep(1);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
packagestudy0528ball;importjava.awt.Graphics2D;importjava.awt.RenderingHints;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.util.ArrayList;importjava.util.Random;importjavax.swing.JPanel;public class BallListener implementsActionListener {privateJPanel centerPanel;privateGraphics2D g;private ArrayList list = new ArrayList();private booleanflag;private Random rand = newRandom();public booleanisFlag() {returnflag;
}publicBallListener(JPanel centerPanel) {this.centerPanel =centerPanel;
g= (Graphics2D) centerPanel.getGraphics();//强制转型//设置取表画笔的锯齿状
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
@SuppressWarnings("deprecation")
@Overridepublic voidactionPerformed(ActionEvent e) {if (e.getActionCommand().equals("添加")) {int x = rand.nextInt(centerPanel.getWidth() - 24) + 12;int y = rand.nextInt(centerPanel.getHeight() - 24) + 12;//创建小球对象
Ball ball = new Ball(list, x, y, centerPanel, this);
ball.start();
list.add(ball);
System.out.println(list.size());
flag= true;
}else if (e.getActionCommand().equals("移除")) {//随机溢出小球
if (list.size() > 0) {int index =rand.nextInt(list.size());
Ball ball=list.remove(index);
ball.draw(g);
ball.stop();
System.out.println(ball);
}elseSystem.out.println("没有可移除的小球!");
}else if (e.getActionCommand().equals("暂停")) {if (flag == false) {
flag= true;
}else if (flag == true) {
flag= false;
}for (int i = 0; i < list.size(); i++) {
Ball ball=list.get(i);
ball.setFlag(flag);
}
}
}
}