前言
从零开始学习的Java的第一个月,在面向对象(OOP)的学习中,跟着讲师完成了飞机大战的游戏编码。第二个月开始接触API,心血来潮便结合API中的集合、多线程开始植物大战僵尸的编码,从游戏模式的设计到游戏内容的实现大约花费2个礼拜的时间。如今是我接触Java第7个月,回头来看曾经自己引以为豪甚至在一定程度上帮助自己拿到offer的作品,代码显得稚嫩、生涩。本来想好好改造一番代码,在新增一些未完成的功能,但又不忍心动刀,毕竟是自己的第一个完整作品。于是乎想把设计思路和游戏代码都分享出来,和大家一起互相交流学习。
游戏设计
植物大战僵尸中有一个小游戏关卡,屏幕的正上方有一个滚轮机,会随机生成植物,玩家可以选中植物后自由选择草坪来进行安放。基于此游戏模式,我将该关卡抽取出来,单独做成了一个简易版的植物大战僵尸。游戏的画面大概如下
屏幕左侧会自动生成植物的卡牌,单击选中后可以放置在草坪上。右侧会自动生成僵尸,不同的僵尸移动速度不同,血量不同,还有的僵尸有隐藏奖励,比如:全屏僵尸静止、全屏僵尸死亡等。当时竟然没有做游戏的暂停的功能,导致现在截图的时机很难把控,那这里就先说一下游戏暂停的功能应该怎么做吧。
最简单的一种暂停方式是鼠标移出屏幕,游戏暂停。所以这里需要引入一个鼠标监听器事件。
public void mouseMoved(MouseEvent e) {
// 当游戏处于运行状态时
if (status == start) {
// 通过鼠标移动事件的对象获取当前鼠标的位置
int x = e.getX();
int y = e.getY();
// 如果鼠标超出了游戏界面
if (x > Game.WIDTH || y > Game.HEIGHT) {
// 将游戏的状态改为暂停状态
status = pause;
}
}
}
当然,这只是一个简单的通过监听鼠标的位置来改变游戏状态方法。还可以使用键盘监听器,当按下某个键时游戏暂停,这样的用户体验更好。但原理是一样的,这里就不展示代码了。
游戏对象
首先分析一下游戏中有哪些对象。各式各样的植物,各式各样的僵尸,各式各样的子弹。那么这里就可以抽出三个父类,分别是植物、僵尸、子弹。在面向对象中,子类将继承父类所有的属性和方法。所以可以将三大类中,共有的属性和方法抽到各自的父类中。比如僵尸父类
public abstract class Zombie {
// 僵尸父类
// 僵尸共有的属性
protected int width;
protected int height;
protected int live;
protected int x;
protected int y;
......
// 僵尸的状态
public static final int LIFE = 0;
public static final int ATTACK = 1;
public static final int DEAD = 2;
protected int state = LIFE;
/*
* 这里补充一下为什么父类是抽象类,比如每个僵尸都有移动方法,
* 但每个僵尸的移动方式是不同,所以该方法的方法体可能是不同的,
* 抽象方法没有方法体,在子类中再去进行重写就可以了,
* 但有抽象方法的类必须是抽象类,因此父类一般都是抽象类
*/
// 移动方式
public abstract void step();
....
}
植物父类、子弹父类就同理可得了。
上面说到子类共有的方法需要抽到父类中,那么部分子类共有的方法该如何处理呢?比如,豌豆射手、寒冰射手可以发射子弹,坚果墙就没有射击的这个行为。所以这里就需要用到接口(Interface)。
public interface Shoot {
// 射击接口 - 将部分子类共有的行为抽取到接口中
// 接口中的方法默认是public abstract的,规范的编码应该将该字段舍去
public abstract Bullet[] shoot();
}
到此为止,游戏对象的属性、方法基本都定义完了,至于图片的显示以及如何将图片画出来,只需要使用相应的API即可,这里就不做描述了。工作一年回过来看看,这里能优化的地方还有很多,比如对象的血量、攻击力、移动等都可以统统写入到配置文件中,这样在做游戏参数的调整时,不需要去修改代码相关的内容,只需要修改配置文件里面的参数即可。
游戏内容
现在我们有了游戏的对象,该开始让对象加入到游戏中来,接着让他们动起来,最后还得让他们打起来。首先,让对象加入到游戏中来我是这么做的,这里还是以僵尸为例
// 首先有要一个僵尸的集合
// 僵尸集合
private List<Zombie> zombies = new ArrayList<Zombie>();
// 接着定义随机生成僵尸方法
public Zombie nextOneZombie() {
Random rand = new Random();
// 控制不同种类僵尸出现的概率
int type = rand.nex