一、实验目的
1、掌握 Swing 图形用户界面编程以及事件处理等,掌握 java 绘图技术。
2、掌握多线程编程的基本原理,能使用 Runnable、ExecutorService 等接口进行
线程的创建、启动等工作。
3、培养独立查找资料,并解决问题的能力。
二、实验任务
1、设计并编程实现弹球游戏:
用户能通过 GUI 组件指定生成小球的数量,每个小球将从随机的位置出现,
并具有随机颜色,随机速度以及随机的运动方向,小球沿初始方向匀速运动,当
碰到窗口边缘时,小球将依据受力原理改变运动方向(可简化考虑,受力只改变
小球的运动方向,小球仍按照初始速度匀速运动,且不考虑小球之间的碰撞)。
鼠标在界面中显示为方块状,玩家需按住鼠标来回移动以避开运动的小球及
屏幕四周,如果鼠标碰到任一小球或者窗口四周,则游戏结束。
程序需提供计时功能,并最终显示玩家能坚持多少秒。
2、程序要求:
(1) 具备相应界面,并通过事件编程,实现相应的 GUI 组件功能。
(2) 使用多线程技术,在程序窗口区域绘制小球,并以线程控制小球的移动,
实现动画效果。
(3) 实现鼠标与屏幕四周,以及与小球的碰撞检测。
三、开发工具与平台
1.开发工具:Eclipse 默认是一个和 Jbuilder 类似的 Java 开发工具,但它不仅仅只
是 Java 开发工具,只要装上相应的插件,eclipse 也可作为其它语言的开发工具。
如 C/C++插件(CDT)。
2.开发平台:JDK1.5
四、设计思路
1.界面设计
(1)制作一个简单的面板 JFrame,文件保存为 bollFrame.java
其中为一 public 的类 bollFrame,其构造方法为:
bollFrame(int n){
super();
setTitle("我的弹球小游戏");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();// 得 到
电脑屏幕大小
setSize(450,450);
setLocation((dimension.width-game.getWidth())/2-250,
(dimension.height-game.getHeight())/2-250);//设置面板显示基中;
this.n = n;
myBollPanel = new bollPanel(n);//构造一个画板;
add(myBollPanel);//将画板放入 JFrame
createMenu();//创建菜单;
setJMenuBar(bar);
}
(2)构造画板类,文件保存为 bollPanel.java
其构造函数如下:
bollPanel(int n){
this.n = n;
// executorThread = Executors.newCachedThreadPool();//创建线程池;
mouse = new mouseThread(mxNow,myNow,groupThread,this);//启动鼠标
线程;
this.setIsOver(false);//游戏开始线程条件的判断;
for(int i =0 ;i
myBollThread =new bollThread(this);
groupThread.add(myBollThread);
//executorThread.execute(myBollThread);//小球线程加入线程池;
}
addMouseListener(this);
addMouseMotionListener(this);
}
Paint()方法如下构造:
public void paint(Graphics g){
if(!this.getIsOver())
{
//如果游戏还没结束;
super.paint(g);//清除原先的图像
g.setColor(Color.RED);
g.setFont(new Font("宋体",Font.BOLD+Font.ITALIC,15));
g.drawString("你已坚持:"+getT()+"S", 15, 15);//计时器的显示;
for(int i = 0;i(getWidth()-23)||xNow<0){//碰到左右边界;
groupThread.get(i).dx = -groupThread.get(i).dx;
}
if(yNow>(getHeight()-23)||yNow<0){//碰到上下边界;
groupThread.get(i).dy = -groupThread.get(i).dy;
}
设置下一个位置;
groupThread.get(i).setxNow(xNow+groupThread.get(i).getdx());//
groupThread.get(i).setyNow(yNow+groupThread.get(i).getdy());
}
}
if(isMouse){
g.drawImage(new ImageIcon("boll.gif").getImage(), mxNow,
myNow, 40,40, this);//鼠标的图像;
}
}//end paint();
总体界面如下:
2.逻辑设计
(1).首先,我们考虑到多个小球的运动,实质上是多线程的使用,n 个小球我们
就同时启动 n 个线程去控制每个小球的运动。因此我构造出一个类作为一个小球
的线程,保存文件为 bollThread.java,顾名思义,小球线程类。相关构造方法如
下:
public bollThread(bollPanel my){
this.myPanel = my;
xStart = data.nextInt(200);//随机产生开始位置;
yStart = data.nextInt(200);
setxNow(xStart);////设置现在的位置;
setyNow(yStart);
setdx();//设置随机方向使用;注意:若不设置为随机,则所有小球将
相同一个方向运动;
setdy();
}
(2)再次,小球的出现需要有载体,这就是画板,前面界面设置中已提到过,通
过让多个小球共享一个画板,以及通过线程控制小球的重绘,最后通过画板显示
给用户。
(3)在第(2)点中提到的画板上我们需要实现鼠标的监听,以下给出几个重要的
鼠标事件监听:
public void mousePressed(MouseEvent e) {//单击启动鼠标事件;
if(e.getButton()==MouseEvent.BUTTON1){
copy.schedule(new task(), 0,1000);//启动定时器;
for(int i =0 ;i
mxNow = e.getX();// TODO Auto-generated method stub
myNow = e.getY();
if(mxNow>(this.getWidth()-30)||mxNow<=0||myNow>(this.getHeight()-30)||myNow
<=0){//鼠标是否碰到边界;
// executorThread.shutdown();
this.setIsOver(true);
copy.cancel();
JOptionPane.showMessageDialog(null, "你撞墙了,下次小
心点。。。游戏结束","提示" ,JOptionPane.OK_OPTION);
this.removeMouseListener(this);
this.removeMouseMotionListener(this);
}
for(int i =0 ;igroup,bollPanel bollpane){
this.mxNow = mxNow;//传入鼠标现在位置;
this.myNow = myNow;
this.groupThread = group;//传入小球线程组;
this.bollpane = bollpane;//传入画板对象;
}
3.程序测试
在程序测试中,先后测试出了,以下几个注意点:
A.在判断小球与鼠标控制的小球(“眼睛”)相撞时,条件中用到两点之间的距离
问题,发现条件不能是两点距离刚好等于两相撞物体半径和,此条件太苛刻,可
能引起判断的失误(概率蛮高的呵呵),而应该是小于半径和。
B..在小球碰撞或控制的小球(”眼睛“)碰到边界游戏结束时,已经停止了线
程,小球竟然还能动,而且是缓慢移动,但是判断是否碰撞及边界问题都能正常
执行,与我所意料的完全相反,到现在也不知道是怎么回事,只能在 paint() 里
进行条件判断,只让 bollPanel 留下最后一次的个小球及“眼睛”位置。
C.这是最后才调试出来的,也是这个游戏中较难注意到的。
就是一些 boolean 变量的设置:
private boolean isTime;//定时器是否启动;
private boolean isGroupThread;//所有小球线程组是否启动;
private boolean isMouseThread;//鼠标线程是否启动;
private boolean isMouse;//鼠标事件是否启动;
private boolean isOver;//游戏是否结束;
刚开始时并没有注意到这些,如果我点击鼠标左键开始游戏时,启动了小球
线程,这时要设置变量 isGroupThread = true 标志小球线程组已启动,释放鼠
标启动鼠标线程时,isMouseThread = true 让鼠标线程来控制与小球相撞的判
断,定时器也在这时响应启动 isTime =true 开始计时,若不设置这些的话,将
发生以下本质上的共同错误:
:等我释放鼠标后想再次控制小球,就必须在一次启动鼠标事件,这时将重
新启动一次计时器,重新启动一次小球线程组,重新启动鼠标线程;这样的结果
是导致游戏的结果不确定性,没碰到小球(实际上已碰撞)它就检测碰撞结束游
戏。以及计时器的计时紊乱等。
具体调试后的相关代码实现见附录.。
五、实验总结
通过这次的程序设计,我更加明白了学以致用的道理,对线程的应用使得
我更好更快的掌握了线程的本质,同时,在此次的程序设计中,经过不断地调试,
是我明白了一个程序或项目要做到“精致”,是是需要花多大的精力和耐心,总
之,学以致用,会将学到的知识应用到实际中才是学之根本。
此次实验也更了解到了线程的知识,比如:一个线程 stop 后,不能再用 start
启动,而应当重新实例化,在 start()。也就是说线程的状态转换过程(新建-就绪
-运行-阻塞-死忙)是不可逆的,这是个人理解,至少从本实验中可以得到验证。