Java实现植物大战僵尸简易版(代码+注释分析)
看前了解
这篇博文我将使用面向对象的方法介绍如何编写这个小游戏,当然这个小游戏是我的第二个小项目,所以此项目写完后也是迫不及待的发给大家学习和交流!!!
下面是一段代码运行视频
Java植物大战僵尸演示视频
一、代码编写前游戏结构以及代码结构分析
- 基类Object类
- 继承类(超类)
- 植物类
FatherPlant
- 僵尸类
FatherZombie
- 子弹类
FatherBullet
- 植物类
- 各类型子类继承对应类型父类
下面是各类型超类的代码
- 植物类
FatherPlant
package game.father_plant;
import java.util.ArrayList;
import game.painting.Painting;
/**
* 所有植物的超类,这个类提供了绝大部分植物的共同属性和方法
*/
public abstract class FatherPlant {
protected boolean bool = false; // 植物状态判断
protected String name; // 植物名字
protected int x; // 植物x轴坐标
protected int y; // 植物y轴坐标
protected ArrayList<String> url; // 植物常规状态图片集合
protected ArrayList<String> url2; // 植物攻击状态图片集合
protected Painting p;
protected int count = 0;
protected int HP = 200; // 植物血量
public FatherPlant() {
}
public FatherPlant(String name, int x, int y, Painting p) {
this.setName(name);
this.setX(x);
this.setY(y);
this.setP(p);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void setP(Painting p) {
this.p = p;
}
public void setBool(boolean b) {
bool = b;
}
public boolean getBool() {
return bool;
}
public int getCount() {
// 返回用来提取帧图片的索引
if (count >= getUrl().size()) {
count = 0;
return count;
}
return count++;
}
/**
* @return 如果植物是单一属性不具备攻击需要重写
*/
public ArrayList<String> getUrl() {
if (!bool) {
return url;
}
return url2;
}
public void setHP(int HP) {
this.HP = HP;
}
public int getHP() {
return HP;
}
public abstract void AttackMethods(); // 植物的攻击方法
}
- 僵尸类
FatherZombie
package game.father_zombie;
import java.util.ArrayList;
import game.painting.Painting;
/**
* 所有植物的超类,这个类提供了绝大部分植物的共同属性和方法
*/
public abstract class FatherZombie {
protected String name; // 僵尸名字
protected int x; // 僵尸x轴坐标
protected int y; // 僵尸y轴坐标
protected ArrayList<String> url; // 僵尸图片地址集合
protected ArrayList<String> url2; // 僵尸攻击集合
protected Painting p;
protected int count = 0;
protected int HP = 200;
protected int AD = 20;
protected boolean bool = false;
public FatherZombie(String name, int x, int y, Painting p) {
this.setName(name);
this.setX(x);
this.setY(y);
this.setUrl(url);
this.setP(p);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public ArrayList<String> getUrl() {
if (bool) {
return url2;
}
return url;
}
public void setUrl(ArrayList<String> url) {
this.url = url;
}
public Painting getP() {
return p;
}
public void setP(Painting p) {
this.p = p;
}
public void setHP(int HP) {
this.HP = HP;
}
public int getHP() {
return HP;
}
public int getCount() {
// 返回用来提取帧图片的索引
if (count >= getUrl().size()) {
count = 0;
return count;
}
return count++;
}
public void setBool(boolean bool) {
this.bool = bool;
}
public boolean getBool() {
return this.bool;
}
public void setAD(int AD) {
this.AD = AD;
}
public int getAD() {
return AD;
}
public abstract void AttackMethods(); // 植物的攻击方法
}
- 子弹类
FatherBullet
package game.fatherbullet;
import java.util.ArrayList;
/**
* 子弹类的父类,所有子弹类继承该父类
*/
public class FatherBullet {
protected String name;
protected int x; // 子弹x轴位置
protected int y; // 子弹y轴位置
protected ArrayList<String> url; // 子弹图片路径
protected int count = 0; // 子弹图片集合索引
protected int AD = 20; // 子弹默认伤害
public FatherBullet(String name, int x, int y) {
this.setName(name);
this.setX(x);
this.setY(y);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public ArrayList<String> getUrl() {
return url;
}
public void setUrl(ArrayList<String> url) {
this.url = url;
}
public int getCount() {
if (count >= url.size()) {
count = 0;
}
return count++;
}
public void setCount(int count) {
this.count = count;
}
public void setAD(int AD) {
this.AD = AD;
}
public int getAD() {
return AD;
}
}
二、代码分析
- 如果你看了前面的代码之后你就会发现一个规律,就是所有的超类都有共同的成员属性
protected String name;
protected int x;
protected int y;
protected ArrayList<String> url;
protected int count = 0;
这些成员属性是所有超类共有的,name是对象的名称,x和y分别是对象的当前x轴和y轴坐标,集合url是当前对象的帧图片地址存放集合,count是遍历集合用的索引,当然有的超类还有url2,比如植物和僵尸类就至少有两个url集合,因为他们的动作不唯一,有攻击和常规两种或者更多种形态,这上面提到的是基本属性,也就是一个超类必要的属性,后续代码可能会有添加,下面我们看一下三个超类的公共属性,大家可以思考一下!
FatherPlant
植物类
protected boolean bool = false; // 植物状态判断
protected String name; // 植物名字
protected int x; // 植物x轴坐标
protected int y; // 植物y轴坐标
protected ArrayList<String> url; // 植物常规状态图片集合
protected ArrayList<String> url2; // 植物攻击状态图片集合
protected Painting p;
protected int count = 0;
protected int HP = 200; // 植物血量
FatherZombie
僵尸类
protected String name; // 僵尸名字
protected int x; // 僵尸x轴坐标
protected int y; // 僵尸y轴坐标
protected ArrayList<String> url; // 僵尸图片地址集合
protected ArrayList<String> url2; // 僵尸攻击集合
protected Painting p;
protected int count = 0;
protected int HP = 200;
protected int AD = 20;
protected boolean bool = false;
FatherBullet
子弹类
protected String name;
protected int x; // 子弹x轴位置
protected int y; // 子弹y轴位置
protected ArrayList<String> url; // 子弹图片路径
protected int count = 0; // 子弹图片集合索引
protected int AD = 20; // 子弹默认伤害
植物类和僵尸类都有HP(血量)值,不同的是僵尸类有AD(伤害)值,子弹类只有一个AD值,因为子弹对象只有一个功能就是碰撞到僵尸对僵尸对象进行伤害打击,僵尸对象的伤害也是对植物的,这里的值未必就是确定的,具体伤害和血量值可以在子类继承相对应的父类的时候进行重写。
- 子类继承父类的基本属性,别且根据自身情况进行一定情况的重写父类的属性和方法,这里举两个例子,一个是机枪射手和一个橄榄球僵尸代码,代码如下:
MachineGunShooter
类
package game.plants;
import game.bullet.Fireball;
import game.father_plant.FatherPlant;
import game.painting.Painting;
import java.util.ArrayList;
public class MachineGunShooter extends FatherPlant implements Runnable {
// 机枪射手
private int miss = 0;
{
HP = 1000;
url = new ArrayList<>();
url2 = new ArrayList<>();
for (int i = 0; i < 25; i++) {
url.add("src/game/images/plant_images/jiqiang/changgui/jiqiangsheshou"+ i +".png");
}
for (int i = 0; i < 49; i++) {
url2.add("src/game/images/plant_images/jiqiang/gongji/jiqianggongji"+ i +".png");
}
}
public MachineGunShooter(String name, int x, int y, Painting p) {
this.setName(name);
this.setX(x);
this.setY(y);
this.setP(p);
}
@Override
public void AttackMethods() {
if (bool) {
if (miss++ % 30 == 0) {
Thread thread = new Thread(this);
thread.start();
}
}
}
@Override
public void run() {
for (int i = 0; i < 4; i++) {
p.BulletAdd(new Fireball("火球子弹", getX() + 80, getY()));
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
RugbyZombies
类
package game.zombie;
import game.father_zombie.FatherZombie;
import game.painting.Painting;
import java.util.ArrayList;
public class RugbyZombies extends FatherZombie {
// 橄榄球僵尸类
{
HP = 800;
AD = 50;
// 初始化僵尸帧
url = new ArrayList<>();
url2 = new ArrayList<>();
for (int i = 0; i < 30; i++) {
url.add("src/game/images/zombie_images/ganlanqiu/zoulu/ganlanqiujiangshi"+ i +".png");
}
for (int i = 0; i < 36; i++) {
url2.add("src/game/images/zombie_images/ganlanqiu/gongji/ganlanqiugongji"+ i +".png");
}
}
public Rugby_Zombies(String name, int x, int y, Painting p) {
super(name, x, y, p);
}
@Override
public void AttackMethods() {
}
}
这两个类分别是植物和僵尸的类,它们都有的共同特点就是属性重写,和url帧图片集合的初始化赋值,这将使它们有属于自己的属性。
- 上面的介绍已经告诉你,父类的定义和子类的重写,图片是自己收集的,后面我再来聊游戏中所需资源的问题,现在我们要分析的是游戏的绘制问题,我把大部分游戏逻辑都写在绘制游戏的面板类中了,这不是一个很好的习惯,由于我经验不够丰富,加上之前一直写算法题所以坏习惯没有改掉,不过这也不是不行,只是结构分层没有那么清晰,游戏逻辑和面板写再一起的代码会比较长,不过代码注释我都有,大可不必担心可以慢慢品,