用两天时间试着用JAVA模拟植物大战僵尸写的游戏
程序只用了2天时间完成,BUG较多,也没有做过多优化,功能也很简单,最后也没有想去改进
源代码免费提供,CSDN上的需要积分下载,可以去GIT上下载**
GIT地址:https://github.com/akh5/JAVA/tree/master/Plants
只有当左下角图标亮起时才能安放豌豆射手
主要代码
父类MovingObject,包含所有判断和图片读取相关代码
package Plants;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
public abstract class MovingObject {
public static final int LIFE = 0;
public static final int DEAD = 1;
public static final int REMOVE = 2;
public static final int EAT = 3 ;
public static final int HIT = 4;
protected int state = LIFE ;//当前状态(默认为活着)
protected int width; //宽
protected int height; //高
protected int x; //横坐标
protected int y; //纵坐标
/*僵尸,植物的构造方法*/
public MovingObject(int width,int height) {
this.width = width;
this.height = height;
}
public MovingObject(int width , int height , int x , int y){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
public abstract void step();
//获取对象的图片
public abstract BufferedImage getImage();
//判断对象是否是活着
public boolean isLife() {
return state == LIFE ; //当前状态为LIFE 表示对象活着
}
//判断对象是否是死着
public boolean isDead() {
return state == DEAD ; //当前状态为LIFE 表示对象活着
}
//判断对象是否删除
public boolean isRemove() {
return state == REMOVE ; //当前状态为LIFE 表示对象活着
}
public boolean isEat(){
return state == EAT;
}
public boolean isHit(){
return state == HIT ;
}
public boolean outofBounds() {
return this.y>=World.HEIGHT;
}
public void paintObject(Graphics g) {
g.drawImage(this.getImage(), this.x,this.y,null);
}
// /*判断敌人是否越界*/
// public boolean outofBounds() {
// //return this.y>=World.HEIGHT;
// }
/*碰撞检测 this是敌人 other是子弹*/
public boolean hit(MovingObject other) {
int x1 = this.x-other.width; //
int x2 = this.x+this.width; //
int y1 = this.y-other.height;
int y2 = this.y+this.height;
int x = other.x;
int y = other.y;
//x在x1与x2之间,且y在y1与y2之间,即为撞上了
return x>=x1&&x<=x2&&y>=y1&&y<=y2;
}
public void goDead() {
state = DEAD; //将当前状态修改为DEAD
}
public void goEat(){
state = EAT;
}
public void goHit(){
state = HIT;
}
}
图片Image类,存放图片路径
这里用切换图片的方法让图片动起来,植物和僵尸有十几张按帧截出来图,循环切换图片达到动图效果
package Plants;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.IOException;
public class Images {
public static BufferedImage background; //背景图
public static BufferedImage bullets; //子弹图
public static BufferedImage[] sun; //太阳图
public static BufferedImage[] plants; //植物切换
public static BufferedImage[] zombies; //僵尸
public static BufferedImage select0; //背景图
public static BufferedImage select1;
public static BufferedImage[] eat;
public static BufferedImage[] die;
public static BufferedImage start;
public static BufferedImage gameover;
public static BufferedImage sunback;
static{
background = readImage("background.png");
bullets = readImage("bullet.png");
sun = new BufferedImage[10];
for(int i = 0; i< 10;i++){
sun[i] = readImage("Sun"+i+".png");
}
select0 = readImage("select0.png");
select1 = readImage("select1.png");
sunback = readImage("SunBack.png");
eat = new BufferedImage[4];
for(int i = 0;i<4;i++){
eat[i] = readImage("eat"+i+".png");
}
start = readImage("start.png");
gameover = readImage("gameover.png");
die = new BufferedImage[3];
for (int i = 0; i<3;i++){
die[i] = readImage("die"+i+".png");
}
plants = new BufferedImage[13];
for(int i = 0;i<13;i++){
plants[i] = readImage("Peashooter["+(i+1)+"].png");
}
zombies = new BufferedImage[18];
for(int i = 0 ;i<18;i++){
zombies[i] = readImage("Zombie["+(i+1)+"].png");
}
}
public static BufferedImage readImage(String fileName){
//同包下读取图片
try {
BufferedImage img = ImageIO.read(MovingObject.class.getResource(fileName));
return img;
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException();
}
}
}
子类Plant,豌豆射手图片切换,以及图片宽高,坐标
package Plants;
import java.awt.image.BufferedImage;
public class Plants extends MovingObject {
private int life;
private int getx;
private int gety;
public int moveable = 1;
private int timer = 0;
public Plants(int x,int y){
super(90,90,x,y);
life = 3;
}
@Override
public void step() {
}
int index = 5;
@Override
public BufferedImage getImage() {// 每十毫秒走一次
if(isLife()) {
BufferedImage img = Images.plants[index++%13];
//System.out.println(index);
return img;
}
if(isDead()) {
return null;
}
return null;
/* index = 0;
* 10M 返回Images.heros[0] index = 1
* 20M 返回Images.heros[1] index = 2
* 30M 返回Images.heros[0] index = 3
* 40M 返回Images.heros[1] index = 4
* */
}
public void subtaractLife() {
if(timer++%2 == 0)
life--;
if(life<0){
goDead();
state =REMOVE;
}
}
}
子类Zombies,效果同Plants
package Plants;
import java.awt.image.BufferedImage;
import java.util.Random;
public class Zombies extends MovingObject {
private int speed;
private int timer = 0;
private int life = 3;
public Zombies(int getx,int gety) {
super(100, 120,getx,gety);
speed = 2;
}
@Override
public void step() {
if(!isEat()&&timer<50)
x-=speed;
if (timer>50)
x-=speed;
}
int index = 0;
@Override
public BufferedImage getImage() {
if(isLife()) {
return Images.zombies[index++%18];
}
if(isEat()&&timer++<50){
return Images.eat[index++%4];
}
if(timer>=50){
state = LIFE;
}
if(isLife()) {
return Images.zombies[index++%4];
}
if(isDead()){
int i = 0;
state = REMOVE;
return Images.die[i++];
// state = REMOVE;
// return img;
}
return null; //删除状态,直接返回NULL;
}
}
子类back,背景图
package Plants;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class Back extends MovingObject {
public Back() {
super(World.WIDTH, World.HEIGHT, 0, 0);
}
@Override
public void step() {
}
@Override
public BufferedImage getImage() {
return Images.background;
}
public void paintObject(Graphics g) {
g.drawImage(this.getImage(), this.x, this.y, null);
}
}
子类Sun
package Plants;
import java.awt.image.BufferedImage;
import java.util.Random;
public class Sun extends MovingObject {
public Sun(int x,int y) {
super(100,100,x,y);
Random rand = new Random();
x = rand.nextInt(World.WIDTH-this.width);
}
@Override
public void step() {
y+=3;
}
int index = 0;
@Override
public BufferedImage getImage() {
return Images.sun[index++%9];
}
}
主类World,主要代码,全部功能的实现类,僵尸入场,鼠标监听,这里只设定了鼠标点击操作,判定当前太阳值,能否安防豌豆射手。用数组方式存放所有对象,动态扩容
package Plants;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JFrame; //相框
import javax.swing.JPanel; //相坂 ,面板
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.event.MouseAdapter; // 鼠标监听器
import java.awt.event.MouseEvent; //监听事件,鼠标的坐标封装在内
public class World extends JPanel{
public static final int WIDTH = 1434;
public static final int HEIGHT = 780;
private Back back = new Back();
private Plants[] plants = {};
private Bullets[] bullets = {};
private MovingObject[] Zombiesun = {};
private int Sun = 50;
private int score = 0;
public static final int START = 0;
public static final int RUNNING = 1;
// public static final int PAUSE = 2;
public static final int GAME_OVER = 3;
private int state = START; //当前状态默认为启动状态
public MovingObject nextOne() {
Random rand = new Random();
int type = rand.nextInt(33);
if(type <4) {
return new Zombies(1434,70);
}
else if(type <8 ) {
return new Zombies(1434,220);
}
else if(type < 24) {
Sun+=50;
Random sun = new Random();
int locatex = rand.nextInt(400)+1000;
return new Sun(locatex,0);
}
else if(type < 28) {
return new Zombies(1434,470);
}
else if(type < 32) {
return new Zombies(1434,590);
}else {
return new Zombies(1434,340);
}
}
public Plants nextPlant(int x,int y) {
return new Plants(x,y);
}
int enterIndex = 0; //敌人入场计数器
public void enterAction() {
enterIndex++;
if(enterIndex%40 == 0) { //每400(10M*40)毫秒走一次
MovingObject obj = nextOne(); //获取敌人对象
Zombiesun = Arrays.copyOf(Zombiesun, Zombiesun.length+1);//扩容长度
Zombiesun[Zombiesun.length-1] = obj;
}
}
int plantIndex = 0;
public void plantAction(int x,int y){
Plants obj = nextPlant(x,y); //获取敌人对象
plants = Arrays.copyOf(plants, plants.length+1);//扩容长度
plants[plants.length-1] = obj;
// }
}
public void stepAction() {
for(int i = 0; i < Zombiesun.length;i++) {
Zombiesun[i].step();
}
for(int i = 0;i < bullets.length;i++) {
bullets[i].step();
}
}
public void bulletHitAction() {
for(int i = 0 ; i<bullets.length;i++) {
Bullets b = bullets[i];
for(int j = 0; j < Zombiesun.length;j++) {
MovingObject f = Zombiesun[j];
if(b.isLife()&&f.isLife()&&f.hit(b)) {
b.goDead();
f.goDead();
score+=10;
}
}
}
}
public void PlantsMeetAction() {
for(int i = 0;i<Zombiesun.length;i++) { // 遍历所有敌人
MovingObject f = Zombiesun[i]; //获取每一个敌人
for(int j = 0;j<plantIndex;j++){
if(plants[j].isLife()&&f.isLife()&&f.hit(plants[j])) {//撞上了
f.goEat();
plants[j].subtaractLife();
}
}
}
}
public void outOfBoundsAction() {
int index = 0;
MovingObject[] zombieLives = new MovingObject[Zombiesun.length];
for(int i = 0 ;i<Zombiesun.length;i++) {
MovingObject f = Zombiesun[i];
//实际向保留的是,不越界的,并且状态是活着的
//死了的是爆破的效果,不要的是REMOVE状态
if(!f.outofBounds()&&!f.isRemove()) { //不越界,判断f.outofbounds()取反的值
zombieLives[index] = f;
index++;
}
}
//把不越界的敌人装到enemi中,此时的长度是index
Zombiesun = Arrays.copyOf(zombieLives, index);
index = 0; //下标不越界的敌人的计数器
//统计不越界的敌人,
Bullets[] bulletLives = new Bullets[bullets.length];
for(int i = 0 ;i<bullets.length;i++) {
Bullets b = bullets[i];
if(!b.outofBounds()&&!b.isRemove()) { //不越界,判断f.outofbounds()取反的值
bulletLives[index] = b;
index++;
}
}
//把不越界的敌人装到enemi中,此时的长度是index
bullets = Arrays.copyOf(bulletLives, index);
}
int SX =-50;
int SY = -50;
public Bullets[] shoot(){
Bullets[] bs = new Bullets[1];
bs[0] = new Bullets(SX,SY);
return bs;
}
int shootIndex = 0; //子弹入场计数器
/*子弹入场*/
public void shootAction() {
shootIndex++;
if(shootIndex%40==0) {
Bullets[] bs = shoot();
System.out.println(bs.length);
//扩容(bs有几个元素,就扩大多少容量)
bullets = Arrays.copyOf(bullets, bullets.length+bs.length);
/*
* bs是原数组
* 0是从原数组bs第一个元素开始
* bullets是目标数组
* bullets.length-bs.length从下标几开始复制
* bs.length代表是复制几个元素
* */
System.arraycopy(bs, 0, bullets, bullets.length-bs.length, bs.length);
}
}
public void checkGameOverAction() { //每10毫秒走一次
for (int i = 0; i<Zombiesun.length;i++) {
if (Zombiesun[i].x < 200) {
try {
UserDao.save("李四", score);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
state = GAME_OVER;
}
}
}
public void action() {
MouseAdapter l = new MouseAdapter() {
// public void mouseMoved(MouseEvent e) {
// int x = e.getX();
// int y = e.getY();
// plants[p].moveto(x, y);
// } //英雄机随着鼠标移动了;
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if (state == START)
state = RUNNING;
if (state == RUNNING) {
int sx;
int sy;
if (Sun >= 100) {
sx = e.getX() - 45;
sy = e.getY() - 45;
plantAction(sx, sy);
SX =sx+65;
SY = sy+6;
plantIndex++;
if(Sun>10) {
shoot();
}
Sun -= 100;
}
// Sun+=50;
}
}
};
this.addMouseListener(l); //处理鼠标的操作事件
this.addMouseMotionListener(l); //处理鼠标滑动事件
Timer timer = new Timer();
int intervel = 50;
timer.schedule(new TimerTask() {
public void run() {
if(state == RUNNING) {
enterAction();
shootAction();
PlantsMeetAction();
stepAction();
bulletHitAction();
outOfBoundsAction();
checkGameOverAction();
repaint(); //所有事件都在repain之前完成
//System.out.println(bullets.length);
}
}
}, intervel, intervel);
}
/** 重写paint()画 g:画笔 在里面调用刚刚写的 paintObject()*/
public void paint(Graphics g){
back.paintObject(g); //画天空
for(int i = 0;i<plants.length;i++){
plants[i].paintObject(g); //画敌人
}
for(int i=0;i<Zombiesun.length;i++){ //遍历所有敌人
Zombiesun[i].paintObject(g); //画敌人
}
for(int i=0;i<bullets.length;i++){ //遍历所有子弹
bullets[i].paintObject(g); //画子弹
}
if(Sun<100){
g.drawImage(Images.select0,10,600,null);
}else{
g.drawImage(Images.select1,10,600,null);
}
if(state == START)
g.drawImage(Images.start,0,0,null);
if(state == GAME_OVER) {
g.drawImage(Images.gameover, 500, 200, null);
g.setFont(new Font("黑体",Font.BOLD,35));
g.drawString("排行榜", 350, 350);
String[] result;
try {
result = UserDao.getScoreAndUsername();
int y = 360;
for(int i = 0;i<10;i++) {//只显示十个数据在窗口上
if(result[i]!=null) {
g.setFont(new Font("",Font.PLAIN,20));
g.drawString(result[i], 500, y);
y+=30;//依次往下挪30给像素,排开
}
}
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
g.drawString("SCORE:"+score,10,25);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
World world = new World();
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH,HEIGHT);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Music m = new Music();
m.start();
world.action();
}
}
音乐Music类,单线程播放背景音乐。
package Plants;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
public class Music extends Thread {
public void run() {
try {
FileInputStream fil1 =
new FileInputStream("src/Plants/Music/Crazy Davy.mp3");
BufferedInputStream bis1 =
new BufferedInputStream(fil1);
Player p = new Player(bis1);
p.play();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JavaLayerException e) {
e.printStackTrace();
}
}
}
数据库相关操作 次要功能
package Plants;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class UserDao {
//1定义数据库驱动
public static String driver="com.mysql.jdbc.Driver" ;
//定义url
public static String url="jdbc:mysql://localhost:3306/shoot";
//定义mysql用户名,每个人的用户在安装mysql的时候是自己定义的
public static String user="root";
//定义mysql设置是的密码
static String password="";
//定义连接对象Connection
public static Connection con;
//定义传输器对象
public static Statement statement;
public static void DBConnection() throws Exception {
//注册数据库
Class.forName(driver);
//获取数据库连接
con=DriverManager.getConnection(url, user, password);
//获取传输器
statement=con.createStatement();
//
}
//将分数存入数据库
public static void save(String name,int score) throws Exception {
try {
DBConnection();
String sql="insert into score(username,score) values('"+name+"',"+score+")";
//利用传输器将sql传入数据库
statement.executeUpdate(sql);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally {
con.close();
}
}
public static String[] getScoreAndUsername() throws Exception {
ResultSet rs = null;
String result[] = new String[100];
try {
DBConnection();
String sql="select username,score from score order by score desc";
rs = statement.executeQuery(sql);
for (int i = 0 ;i<result.length;i++) {
if(rs.next()) {
result[i] = "第"+(i+1)+"名"+rs.getString("username")+":"+rs.getInt("score");
}
}
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally {
con.close();
rs.close();
}
return result;
}
public static void main(String[] args) throws Exception {
String result[] = getScoreAndUsername();
for(int i = 0;i<result.length;i++) {
if(result[i]!=null) {
System.out.println(result[i]);
}
}
}
}
- 简单实现的程序,其中的功能和算法也相对粗糙
BUG也很多,比如没有设置豌豆射手的安放区域,所以豌豆射手是鼠标点哪放哪的,以及子弹的生成也是通过鼠标点击的同时生成的,而且是单线程的操作,所以只能用一次点击鼠标完成操作。
僵尸设定了生命值,但是还是被子弹一碰就死,这里要修改父类godie()相关代码,但是后续不想更新了所以没有修改。
我在僵尸死的时候设定了一组轮播的图片,但是僵尸还是死后一秒直接消失,这里是数组成员REMOVE操作一旦执行就把当前僵尸的元素删除了,所以可以在子弹触碰和删除前延时操作。