JAVA实现简单“伪植物大战僵尸“游戏

用两天时间试着用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操作一旦执行就把当前僵尸的元素删除了,所以可以在子弹触碰和删除前延时操作。
发布了52 篇原创文章 · 获赞 13 · 访问量 5473

猜你喜欢

转载自blog.csdn.net/MPF1230/article/details/96307450