潜艇大战代码改动版

潜艇大战

一、超类SeaObject(派生类有,战舰、敌舰、水雷、炸弹)

     我这里面将一些属性进行了私有化处理,并且提供了get及set方法进行访问,
     提高了程序安全性,还将一些代码继续进行简化处理      
package javabean;

import javax.swing.*;
import java.awt.*;
import java.util.Random;

public abstract class SeaObject {
    //声明常量生死
    public static final int LIVE = 0;
    public static final int DEAD = 1;
    //自身状态
    private int state = LIVE;
    //尺寸、位置、速度
    private int width;
    private int height;
    private int x;
    private int y;
    private int speed;
    //敌舰构造器
    public SeaObject(int width, int height) {
        this.width = width;
        this.height = height;
        x = -width;
        Random random = new Random();
        y = random.nextInt(World.HEIGHT  - 150 - height + 1) + 150;
        speed = random.nextInt(3) + 1;
    }
    //战舰、水雷、炸弹构造器
    public SeaObject(int width, int height, int x, int y, int speed) {
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
        this.speed = speed;
    }
    //方法
    //get及set方法
    public int getWidth() {return width;}
    public int getHeight() {return height;}
    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 int getSpeed() {return speed;}
    //移动  抽象方法
    public abstract void move();
    //获取图片   抽象方法
    public abstract ImageIcon getImage();
    //判断死活
    public boolean isLive() {
        return state == LIVE;
    }
    //让对象去死
    public void goDead() {
        state = DEAD;
    }
    //画对象   g画笔,如果对象还活着则将其图片画出来
    public void paint(Graphics g) {
        if (isLive()) {
            this.getImage().paintIcon(null, g, x, y);
        }
    }
    //越界检测 抽象方法
    public boolean isOutOfBounds() {
        return this.x >= World.WIDTH;
    }
    //击中检测
    public boolean isHit(SeaObject other) {
        //this是潜艇战舰,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;
        return (x >= x1 && x <= x2) && (y >= y1 && y <= y2);
    }
}

二、战舰BattleShip

package javabean;

import javax.swing.*;

public class Battleship extends SeaObject {
    //私有化生命、分数
    private int life;
    private int score;
    //构造器
    public Battleship() {
        super(66, 26, 270, 124, 40);
        life = 5;
    }
    //私有属性life及score的get及set方法
    //返回战舰的生命,显示在游戏界面上
    public int getLife() {return life;}
    //战舰击中了水雷潜艇获得生命,或者被水雷击中减命
    //这里的life形参可以为负值
    public void setLife(int life) {this.life += life;}
    //返回战舰的分数,显示在游戏界面
    public int getScore() {return score;}
    //打掉其他潜艇获得分数
    public void setScore(int score) {this.score += score;}
    //重写move,由于战舰的移动需要侦听器去检测键盘
    //所以就在这里重写一下,去除继承来的抽象方法,防止报错
    //然后在下面重载move方法
    @Override
    public void move(){};
    //左右移动
    //这里是对move的重载
    public void move(boolean flag) {
        if (flag) {
            setX(getX() + getSpeed());//右移
        } else {
            setX(getX() - getSpeed());//左移
        }
    }
    @Override//重写getImage,返回战舰图片
    public ImageIcon getImage() {
        return Images.battleship;
    }
    //战舰发射炸弹:炸弹初始化的位置为战舰所在位置
    public Bomb shootBomb() {
        //战舰的x 加上自身的宽度的二分之一,战舰的y加上自身的高度,就是炸弹出现的位置
        return new Bomb(this.getX() + this.getWidth() / 2, this.getY() + this.getHeight());
    }
    //战舰状态
    public boolean isLive() {
        return life > 0;
    }
}

三、敌舰

1.水雷潜艇Minesubmarine

package javabean;

import javax.swing.*;

public class MineSubmarine extends SeaObject implements EnemyLife{
    //构造器
    public MineSubmarine() {
        super(63, 19);
    }
    @Override//重写move
    //潜艇只能向着x递增的方向前进
    public void move() {
        //setX这些方法可以看SeaObject类的定义
        setX(getX() + getSpeed());
    }
    @Override//重写getImage
    //返回水雷潜艇的图片
    public ImageIcon getImage() {
        return Images.minesubm;
    }
    //发射水雷
    public Mine shootMine() {
        return new Mine(getX(), getY());
    }
    @Override//获得生命
    public int getLifes() {
        //若水雷潜艇被击中则返回1,赋予战舰
        return 1;
    }
}

2.鱼雷潜艇Totpesubmarine

package javabean;

import javax.swing.*;

public class TorpeSubmarine extends SeaObject implements EnemyScore{
    //构造器
    public TorpeSubmarine() {
        super(64, 20);
    }
    @Override//重写move
    public void move() {
        setX(getX() + getSpeed());
    }
    @Override//重写getImage
    public ImageIcon getImage() {
        return Images.torpesubm;
    }
    @Override//重写接口的方法战舰获得分数
    public int getScores() {
        return 200;
    }
}

3.侦察潜艇Obsersubmarine

package javabean;

import javax.swing.*;

public class ObserSubmarine extends SeaObject implements EnemyScore{
    //构造器
    public ObserSubmarine() {
        super(63, 19);
    }

    @Override//重写move
    //潜艇向着x递增的方向移动
    public void move() {
        setX(getX() + getSpeed());
    }
    @Override//重写getImage
    public ImageIcon getImage() {
        return Images.obsersubm;
    }
    @Override
    public int getScores() {
        return 100;
    }
}

四、水雷Mine

package javabean;

import javax.swing.*;

public class Mine extends SeaObject{
    //构造器
    public Mine(int x, int y) {
        super(11, 11, x, y, 1);
    }
    @Override//重写move
    //水雷向着y递减的方向移动
    public void move() {
        setY(getY() - getSpeed());
    }
    @Override//重写getImage
    public ImageIcon getImage() {
        return Images.mine;
    }
    @Override//重写isOutOfBounds
    //返回boolean类型,当水雷的超出了海平面,150是海平面的y
    public boolean isOutOfBounds() {
        return getY() <= 150 - getHeight();
    }
}

五、炸弹Bomb

package javabean;

import javax.swing.*;

public class Bomb extends SeaObject{
    //构造体
    public Bomb(int x, int y) {
        super(9, 12, x, y, 12);
    }

    @Override//重写move
    //炸弹只能向着y增加的方向移动
    public void move() {
        setY(getY() + getSpeed());
    }
    @Override//重写getImage
    public ImageIcon getImage() {
        return Images.bomb;
    }
    @Override//重写越界检测
    public boolean isOutOfBounds() {
        //同一个包不需要import导包
        return getY() >= World.HEIGHT;
    }
}

六、图片类Images

package javabean;

import javax.swing.*;
import java.awt.*;

/**
* @Description:  照片类,专门存放图片
* @Author: addict
* @Date:  2022-10-17  17:00
* @Version: 1.8
*/
public class Images {
    //方法一
    //这里使用常量声明加赋值,注意常量声明的同时一定要赋值否则报错
    public static final ImageIcon sea = new ImageIcon("img/sea.png");
    public static final ImageIcon battleship = new ImageIcon("img/battleship.png");
    public static final ImageIcon bomb = new ImageIcon("img/bomb.png");
    public static final ImageIcon gameover = new ImageIcon("img/gameover.png");
    public static final ImageIcon mine = new ImageIcon("img/mine.png");
    public static final ImageIcon minesubm = new ImageIcon("img/minesubm.png");
    public static final ImageIcon obsersubm = new ImageIcon("img/obsersubm.png");
    public static final ImageIcon torpesubm = new ImageIcon("img/torpesubm.png");
    public static final ImageIcon sea1 = new ImageIcon("img/sea1.png");
    //main用来检测图片的读取是否有误,输出8无误,其余数字有误
    public static void main(String[] args) {
        System.out.println(sea.getImageLoadStatus());
        System.out.println(battleship.getImageLoadStatus());
        System.out.println(bomb.getImageLoadStatus());
        System.out.println(minesubm.getImageLoadStatus());
        System.out.println(mine.getImageLoadStatus());
        System.out.println(torpesubm.getImageLoadStatus());
        System.out.println(obsersubm.getImageLoadStatus());
        System.out.println(gameover.getImageLoadStatus());
    }
}

七、运行类World

package javabean;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class World extends JPanel {
    //由于常量经常被调用,所以声明为public
    //常量定义标准:1.经常使用
    //             2.不再变化
    public static final int WIDTH = 641;
    public static final int HEIGHT = 479;
    //创建游戏状态常量
    public static final int RUNNING = 1;
    public static final int GAME_OVER = 0;

    //私有化游戏状态
    private int state = RUNNING;
    //创建私有化对象
    private Battleship ship = new Battleship();
    private SeaObject[] subms = {};//这样进行数组初始化,这时数组为空,啥也没有,一个元素都不存在!!!
    private Bomb[] bombs = {};
    private Mine[] mines = {};
    //随机生成潜艇,私有化仅自己使用
    private SeaObject getSubms() {
        Random random = new Random();
        int num = random.nextInt(20);
        if (num < 10) {
            return new MineSubmarine();
        } else if (num < 16) {
            return new ObserSubmarine();
        } else {
            return new TorpeSubmarine();
        }
    }
    //潜艇数组元素赋值
    private int submsIndex = 0;
    private void submsAction() {
        submsIndex++;
        if (submsIndex % 50 == 0) {
            //这里生成潜艇然后放进数组中
            SeaObject obj = getSubms();
            subms = Arrays.copyOf(subms, subms.length + 1);//扩容
            subms[subms.length - 1] = obj;
        }
    }
    //水雷数组元素赋值
    private int minesIndex = 0;
    private void minesAction() {
        minesIndex++;
        if (minesIndex % 100 == 0) {
            for (int i = 0; i < subms.length; i++) {
                //这里检测subms[i]是否是由MineSubmrine类向上造型得到
                if (subms[i] instanceof MineSubmarine) {
                    //true的话,就强制将subm[i]转换成MineSubmrine(这里不进行判断就强转的话,编译不报错,运行报错)
                    //强转是为了调用Minesubmrine类的shootMine方法去发射水雷
                    //使用向上造型赋值的变量说到底还是,只能调用超类中的属性、方法,但是当这个方法被new出来的对象重写的话
                    // 就会调用被重写之后的方法
                    MineSubmarine obj = (MineSubmarine)subms[i];
                    mines = Arrays.copyOf(mines, mines.length + 1);
                    mines[mines.length - 1] = obj.shootMine();
                }
            }
        }
    }
    //将死亡或者越界的角色从数组中删除,其余的正常移动
    //这里不可以使用传入SeaObject数组类的方式(别想省代码了)!!!!!
    //形参与实参不是一个东西,
    private void moveAction(){ //每10毫秒走一次
        for(int i=0;i<subms.length;i++){ //遍历所有潜艇
            if(subms[i].isOutOfBounds() || !subms[i].isLive()){ //若越界了,或者,是DEAD状态的
                subms[i] = subms[subms.length-1]; //将越界元素替换为最后一个元素
                subms = Arrays.copyOf(subms,subms.length-1); //缩容
            } else {
                subms[i].move(); //潜艇移动
            }
        }
        for(int i=0;i<mines.length;i++){ //遍历所有水雷
            if(mines[i].isOutOfBounds() || !mines[i].isLive()){ //若越界了,或者,是DEAD状态的
                mines[i] = mines[mines.length-1]; //将越界元素替换为最后一个元素
                mines = Arrays.copyOf(mines,mines.length-1); //缩容
            } else {
                mines[i].move(); //水雷移动
            }
        }
        for(int i=0;i<bombs.length;i++){ //遍历所有炸弹
            if(bombs[i].isOutOfBounds() || !bombs[i].isLive()){ //若越界了,或者,是DEAD状态的
                bombs[i] = bombs[bombs.length-1]; //将越界元素替换为最后一个元素
                bombs = Arrays.copyOf(bombs,bombs.length-1); //缩容
            } else {
                bombs[i].move(); //炸弹移动
            }
        }
    }
    //炸弹击中潜艇
    private void bombBangAction() {
        for (int i = 0; i < bombs.length; i++) {
            Bomb bomb = bombs[i];
            //这里先检测炸弹是否还活着,false的话就不再向下执行浪费时间
                for (int j = 0; j < subms.length; j++) {
                    SeaObject obj = subms[j];
                    //这里检测潜艇是否还活着,且被炸弹击中
                    if (obj.isLive() && obj.isHit(bomb)) {
                        //true的话让他们去死
                        bomb.goDead();
                        obj.goDead();
                        if (obj instanceof EnemyLife) {//被击中潜艇是命
                            EnemyLife el = (EnemyLife)obj;//强转为命的接口
                            ship.setLife(el.getLifes());//玩家获得一条狗命
                        } else {
                            EnemyScore es = (EnemyScore)obj;
                            ship.setScore(es.getScores());
                    }
                }
            }
        }
    }
    //水雷击中战舰
    private void mineBangAction() {
        for (int i = 0; i < mines.length; i++) {
            Mine mine = mines[i];
            //这里的isHit传入的是SeaObject类,直接传入Mine类,有个向上造型的过程
            //也可以这样
            //SeaObject obj = mines[i];
            if (mine.isLive() && ship.isLive() && ship.isHit(mine)) {
                mine.goDead();
                ship.setLife(-1);//被击中之后战舰的setLife方法直接传入-1
            }
        }
    }
    //检测游戏是否应该结束
    private void checkGameOver() {
        if (ship.getLife() == 0) {
            state = GAME_OVER;
        }
    }
    //启动程序
    private void action() {
        //侦听器
        //这里声明了一个World的匿名内部类
        KeyAdapter k = new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_SPACE) {//按键的存储在Java中是16进制
                        Bomb bomb = ship.shootBomb();
                        bombs = Arrays.copyOf(bombs, bombs.length + 1);
                        bombs[bombs.length - 1] = bomb;
                    }
                    if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                        ship.move(false);
                    }
                    if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                        ship.move(true);
                    }
                }
        };
        this.addKeyListener(k);

        Timer timer = new Timer();
        //方法内不允许权限修饰符哦
        //private int interval = 10;
        int interval = 1;
        //这里也同样定义了一个匿名内部类哦
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                moveAction();
                submsAction();
                minesAction();
                bombBangAction();
                mineBangAction();
                checkGameOver();
                repaint();
            }
        }, interval, interval);
    }
    @Override//重写paint,这个方法是一层一层的画上去的,最先画的在最底层,一层一层覆盖  g是画笔
    public void paint(Graphics g) {
        //根据游戏状态进行画面绘制
        switch (state) {
            case GAME_OVER:
                Images.gameover.paintIcon(null, g, 0, 0);
                Images.sea1.paintIcon(null, g, 0, 0);
                //这里放置一张图片用来将之前的窗口显示生命覆盖,当游戏结束时可以显示生命为0
                g.drawString("SCORE: " + ship.getScore(), 200, 50);
                g.drawString("LIFE: " + ship.getLife(), 400, 50);
                break;
            case RUNNING:
                //Images.sea是静态成员的调用方式
                // 不过我为了节省代码行,将这些图片变成了常量,声明加初始化放在一起
                //这里面先画的在底下,后画的将其覆盖
                Images.sea.paintIcon(null, g, 0, 0);
                //这里不可以这样画潜艇哦
                //因为ImageIcon.paintIcon要和上面一样传入四个参数哦
                //ship.getImage().paintIcon(g);
                ship.paint(g);//这里去调用Battleship的超类SeaObject中的方法
                for (int i = 0; i < subms.length; i++) {
                    subms[i].paint(g);//这里的操作同ship一致哦
                }
                for (int i = 0; i < bombs.length; i++) {
                    bombs[i].paint(g);
                }
                for (int i = 0; i < mines.length; i++) {
                    mines[i].paint(g);
                }
                g.drawString("SCORE: " + ship.getScore(), 200, 50);
                g.drawString("LIFE: " + ship.getLife(), 400, 50);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        World world = new World();
        world.setFocusable(true);//这里控制着游戏界面是否侦听键盘
        frame.add(world);
        frame.setSize(World.WIDTH + 6, World.HEIGHT + 28);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);//将窗口显示在屏幕中心
        frame.setResizable(false);//窗口创建之后不可以再次改变chicun
        //将窗口进行显示,这里会自动调用paint
        // World没有重写paint的话,会自动调用java自身的paint方法,
        //重写之后的话就会调用重写后的paint
        frame.setVisible(true);
        world.action();
    }
}

八、接口Interface

1.EnemyScore

package javabean;

public interface EnemyScore {
    int getScores();
}

2.EnemyLife

package javabean;

public interface EnemyLife {
    int getLifes();
}

九、附上百度网盘链接

潜艇大战源码及图片,百度网盘链接,提取码:7144

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值