先贴游戏图。
......................................跳过.........................................
时间加起来也算是用了一天的时间,碰碰撞撞,修修补补终于是把这个游戏给做完了,虽然不完美,也还有bug,不过对自己而言,也算是不错的了。
在之前,自己一直觉得这游戏真的挺麻烦的,所以在动手自己开始做前,还一直在网上找相关视频借鉴借鉴,然而却没有,所以没办法只能自己动手。真正的开始敲下第一行代码时,思路却都已经出现在脑海中,然后一发不可收拾,用一个下午的时间把基本功能都给做完了,然后晚上边玩,边修复一些小的bug,第二天早上把排行榜给做了。
现在回过头来看,不扯这个小游戏,我发现我一直都在小看自己,总是在做某件事前,就给自己打上了有可能不成功的标签,又觉得事情很难。在真正开始做时,好多时候事情真的很简单,只是自己对这一方面的内容不熟悉而已。
自己害怕失败吧...真的,一个年轻人还没开始做事之前就退却了,真是不可理喻啊。慢慢来吧,现在还有时间,趁自己还有可以犯错的资本时,多尝试尝试总是好事。
.......................................分割线......................................
我把所有的图都画在了JLabel上,然后再在面板上显示,看起来很方便,但缺点也是很明显,就是不管是我方飞机,还是敌方飞机,还是子弹,在移动起来,总是一卡一卡的,不流畅,可自己又想不出好的法子了,只能这样做下去。
讲一些重要的代码,不然会显得很繁琐,例如继承了JLabel的MyPlane类,Bullet类就不贴了,一些没必要的注释或者调试的输出就不删了。
GameFrame类实现了KeyListener接口,为的是可以直接操作我方飞机的移动,因为我方飞机的JLabel是直接加载这个上面的。移动键为WSAD,然后对活动范围也加了一些限制,总不能移动到可见界面外去了是吧。
@Override
public void keyTyped(KeyEvent e) {
//获得飞机位置
int x = myplane.getX();
int y = myplane.getY();
//每次移动大小
int speed = 10;
//根据不同指令执行命令
if (e.getKeyChar() == 'W' || e.getKeyChar() == 'w') { // 上移
y -= speed;
} else if (e.getKeyChar() == 'S' || e.getKeyChar() == 's') { // 下移
y += speed;
} else if (e.getKeyChar() == 'A' || e.getKeyChar() == 'a') { // 左移
x -= speed;
} else if (e.getKeyChar() == 'D' || e.getKeyChar() == 'd') { // 右移
x += speed;
}
//设置活动范围
if (x > -5 && x < Constant.WIDTH - 65 && y > 0
&& y < Constant.HEIGHT - 110) {
myplane.setLocation(x, y);
}
// repaint();
}
而对于子弹而言,肯定是伴随着飞机发射的,所以出现的位置肯定是跟飞机位置相关,并且是自动发射,不要用户控制,直接开启个线程,可能因为图片的原因,所以需要对位置稍微进行小的偏移修改。
// 开启子弹线程
private void launch() {
new Thread() {
@Override
public void run() {
//如果为true,则启动线程,否则关闭
while (flagBullet) {
// 给出现时的坐标加上一个值,调整位置
//以飞机坐标为参数,生成一个子弹对象
Bullet bullet = new Bullet(myplane.getX() + 30,
myplane.getY() + 30, GameFrame.this);
// 将子弹加入面板
panel.add(bullet);
// 添加进子弹列表
GameList.bulletList.add(bullet);
// 开始行动
new Thread(bullet).start();
// 每隔300毫秒发射一颗子弹
try {
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
}
而对于敌机,则是随机出现的,当然,也不是可以出现在任意位置,而是固定了几个位置,然后随机的。也给限制了数量,面板最多一次可见多少个,然后没隔10秒增加一个,如果达到上限,则不会新出现飞机。
/*
* 启动敌机线程
*/
private void otherPlane() {
new Thread() {
@Override
public void run() {
//获得第一次出现敌机的时间
long firstTime = System.currentTimeMillis();
//一次最多存在number数量的敌机
int number = 5;
while (flagPlane) {
//获得当前时间
long currentTime = System.currentTimeMillis();
//每10秒多增加一架可以在界面上显示的敌机
if(currentTime - firstTime > 10000){
number ++;
//交换数据
firstTime = currentTime;
}
//System.out.println("OtherPlane's number is : "
//+ GameList.planeList.size());
int size = GameList.planeList.size();
//如果list中的敌机数量小于总共可以在界面上显示的,则创建对象
if (size <= number) {
OtherPlane otherPlane = new OtherPlane(GameFrame.this);
//需要把该对象放到面板中去显示
panel.add(otherPlane);
//加入list
GameList.planeList.add(otherPlane);
//启动,可以活动
new Thread(otherPlane).start();
//没500毫秒生成一个
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}.start();
}
从上面的代码中可以都可以看到一个GameList类,这个类我是为了操作方便而写的,其实肯定是不符合规范的。在里面实现了一些方法,例如子弹打到了敌机,或者敌机与我方飞机碰到了等等。
下面这个方法就是子弹打到了敌机,然后子弹和敌机都消失,但当时在写这个的时候,出现了很多问题,其中最严重的是当子弹和敌机都消失后,我方飞机还是会被本该消失的飞机碰到,调试了好久才发现,虽然敌机从面板上和list中都移除了,但他的线程仍然存在,也就是说我们只是看不到他了,但还是活着的,所以需要把对应的线程结束掉才行。
//停止相碰的子弹和敌机的线程
op.setFlag(false);
bullet.setFlag(false);
// 判断子弹是否与敌机有接触
public static void intersectsBullet(Bullet bullet, GameFrame gameFrame) {
// 创建子弹的矩形
Rectangle bulletRect = new Rectangle(bullet.getX(), bullet.getY(),
bullet.getWidth(), bullet.getHeight());
for (int i = 0; i < planeList.size(); i++) {
// 创建每一个敌机的矩形,逐一进行判断
OtherPlane op = planeList.get(i);
Rectangle planeRect = new Rectangle(op.getX(), op.getY(),
op.getWidth(), op.getHeight());
// 如果两个矩形相交
if (bulletRect.intersects(planeRect)) {
//分数+1
Score.score ++;
//停止相碰的子弹和敌机的线程
op.setFlag(false);
bullet.setFlag(false);
// 从面板中移除掉敌机和子弹
gameFrame.remove(op);
gameFrame.remove(bullet);
// 从list中移除两个对象
bulletList.remove(bullet);
planeList.remove(op);
// 刷新界面
gameFrame.repaint();
}
}
}
从上面的代码可以看出,我判断是否产生接触用的是Rectangle类中的intersects方法,这个方法的意思是判断两个矩形是否相交,所以需要把需要判断的两个对象提取相应的位置,宽,高出来。但问题是,他们的宽和高都是根据图片设置的,所以在碰撞这个问题是,会有点小bug。
结束游戏有两种方式,一种是地方飞机与我方飞机发生了碰撞
//判断敌机和我方飞机是否相撞
public static void intersectsPlane(OtherPlane op, GameFrame gameFrame) {
//创建我方飞机矩形
MyPlane myplane = gameFrame.getMyPlane();
Rectangle myRect = new Rectangle(myplane.getX(), myplane.getY(),
myplane.getWidth(), myplane.getHeight());
//敌方飞机矩形
Rectangle opRect = new Rectangle(op.getX(), op.getY(), op.getWidth(),
op.getHeight());
//如果矩形相交,则发生碰撞,游戏结束
if (opRect.intersects(myRect)) {
gameFrame.remove(op);
gameFrame.remove(myplane);
op.setFlag(false);
//游戏结束
gameOver(gameFrame);
}
}
例外一种是时间到了,在另外一个类中,就是判断倒计时是否归0了。他们都需要共同的代码,需要提取出来,封装成一个方法了。
结束掉所有的线程,并且清除所有的数据,注意的是,也要把我方飞机的数据置null才行。
public static void gameOver(GameFrame gameFrame){
//清除我方飞机数据
gameFrame.setMyPlane(null);
// 终止生成子弹和生成敌机的循环
gameFrame.setFlagBullet(false);
gameFrame.setFlagPlane(false);
//停止时间分数线程
Score.flag = false;
// 清空list
bulletList.clear();
planeList.clear();
//对话框要出现在清理数据之后
String name = JOptionPane.showInputDialog("游戏结束,请输入你的大名:");
Recorder recorder = new Recorder();
recorder.write(name, Score.score);
// 关闭界面
gameFrame.dispose();
// 打开主界面
new MainFrame();
}
因为也同时写了排行榜,所以在游戏结束时,会弹出一个对话框,提示游戏结束,并且输入自己的姓名。而数据会写入到xml文件中,xml文件解析用的是dom方法,很惭愧的是,才学了没多久,竟然就忘了差不多了,还需要翻看笔记才能想起。
写起来没逻辑,整个程序放在了附件,有需要的朋友可以下载,如果有什么好的建议或不足,谢谢在评论中提出。