圆桌面弹球-学习笔记(二)
下面实现主界面类(BallFrame)和业务处理类(BallService)。
主界面类(BallFrame)中定义了:
一个业务处理类(BallService)的对象,通过调用BallService中的方法来处理业务逻辑;
一个初始化方法initialize(),用来初始化游戏的界面;
一个画板类(BallPanel),该内部类继承了JPanel,重写了void paint(Graphics g)画图方法。
//定义一个JFrame的内部类来完成画图功能 class BallPanel extends JPanel{ //私有构造器 private BallPanel(){ } //重写void paint(Graphics g)画图方法 public void paint(Graphics g){ //调用BallService的画图方法 } }
注意:这里不要写成print,是paint,否则就无法重写JPanel的paint()方法了,画板上也绘制不出任何图像,笔者经常犯这种低级错误,图片一直绘制不出来,后来一检查,才发现是函数名写错了!
下面来想办法实现动画效果,动画效果的实现奥秘也在这个绘图函数上,就是每隔很短的一段时间,就重新绘制一次弹球、砖块、挡板、道具。
之前编写坦克大战时,使用的方法是定义一个线程类,在run函数中使用循环结构来实现周期绘图:
class PaintThread implements Runnable{ public void run() { while(true){ repaint(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
这里使用另外一种方法,使用javax.swing.Timer可以设定每隔一段时间周期就重复执行某个task,类似于Windows系统的计划任务或者Linux系统的crobtab。
在帮助文档里找到:
Timer(int delay, ActionListener listener)
创建一个 Timer 并将初始延迟和事件间延迟初始化为 delay 毫秒。
start()
启动 Timer,使它开始向其侦听器发送动作事件。
restart()
重新启动 Timer,取消所有挂起的触发并使它按初始延迟触发。
先定义每0.1秒执行一次的监听器,在定义过程中,改变各游戏对象状态的业务逻辑全部扔给BallService的对象ballService完成,但是在引用ballService时,出现了一个错误:“不能引用另一方法中定义的内部类中非终态变量ballService”。
public class BallFrame extends JFrame{ //此处略去10行代码 public void initialize(){ //此处略去4行代码,若干注释 BallService ballService = new BallService(); //定义每0.1秒执行一次的监听器 ActionListener task = new ActionListener(){ public void actionPerformed(ActionEvent ae){ //改变位置 //不能引用另一方法中定义的内部类中非终态变量ballService ballService.run(); //刷新画板 ballPanel.repaint(); } }; //此处略去14行代码,若干注释 } //此处略去14行代码,若干注释 }
经过分析,将ballService声明为一个类属性,而不是initialize()中的一个局部变量。
public class BallFrame extends JFrame{ //此处略去10行代码 BallService ballService = null; public void initialize(){ //此处略去4行代码,若干注释 ballService = new BallService(); //定义每0.1秒执行一次的监听器 ActionListener task = new ActionListener(){ public void actionPerformed(ActionEvent ae){ //改变位置 ballService.run(); //刷新画板 ballPanel.repaint(); } }; //此处略去14行代码,若干注释 } //此处略去14行代码,若干注释 }
定义好专用的监听器后,创建一个定时器,如下:
//设定每隔一个时间周期就重复执行的某个task if( timer!=null){ //如果不是第一次游戏,重新启动游戏 timer.restart(); }else{ //如果是第一次游戏,新建一个timer timer = new Timer(100,task); //启动timer timer.start(); }
还有就是在游戏中,玩家是通过键盘的左右键来操纵挡板的左右移动的,还需要创建一个键盘监听器:
//验证该窗口是否添加了其它键盘监听器 KeyListener [] keylisteners = this.getKeyListeners(); //如果没有任何其它的键盘监听器,定义键盘监听器 if(keylisteners.length ==0){ KeyListener keyAdapter = new KeyAdapter(){ public void keyPressed(KeyEvent ev){ //移动挡板的坐标 ballService.setStickPos(ke); } }; //注册键盘监听器 this.addKeyListener(keyAdapter); }