Java面向对象战舰小游戏

  • 前言
  • 思路:
  • 战舰项目代码
  • 1. 战舰特征和行为
  • 水雷艇特征和行为
  • 侦查艇特征和行为
  • 鱼雷艇特征和行为
  • 水雷特征和行为
  • 深水炸弹特征和行为
  • 2. 归纳共有特征
  • 3.初始化
  • 4. 画图
  • 5. 重写方法
  • 定时器
  • 新建潜水艇对象
  • 新建水雷
  • 新建深水炸弹
  • 重写所有类的move方法
  • 6. 把超过窗口和相互碰撞的物体删掉
  • 删除超过窗口的对象
  • 删除相互碰撞的物体
  • 7.最终代码
  • Battleship
  • Bomb
  • Images
  • Mine
  • MineSubmarine
  • ObserveSubmarine
  • SeaObject
  • TorpedoSubmarine
  • World



前言

这是面向对象的小游戏,运用的的知识点有:父子类,继承,构造方法,重载,重写,静态块,修饰词

java实现短视频弹幕_初始化

思路:

1.首先写出项目中会运运用到的物体,在这个项目中,我们会用到:
敌人:三种潜艇:水雷潜艇(Mine Submarine),侦查潜艇(Observe Submarine),鱼雷艇(Torpedo Submarine)
玩家:战舰(Battleship)
道具:水雷(Mine),深水炸弹(Bomb)。
2.每个物体写出它们的特征,并把它们共有的特征归纳,放到超类(父类)里面,再把它们共有的行为写出普通方法写到超类里面,如果是行为一样但是每个行为发生的过程不一样,就写成抽象类(abstract)放到超类.
3.进行初始化,定义每个物体的初始坐标,高度等等特征的初始值。注意这时候图片初始值写成静态块,因为静态块初始化会声明类的时候自动执行,调用的时候直接类.变量就直接出来了,复用性较好。其他的例如窗口高,宽等不变的可以直接定义为常量,其他的特征为实例变量。然后添加访问权限
4. 开始画窗口,然后把物体画到窗口里面
5. 重写方法,让物体移动起来,并发射水雷或者炸弹。
6. 把超过窗口,或者爆炸的物体删掉

战舰项目代码

项目名称我建的叫SubmarineDemo,package的名字叫submarine
6个物体的特征和行为

1. 战舰特征和行为

战舰有宽, 高, x坐标, y坐标, 生命数量, 移动速度
方法是左右移动,发射深水炸弹,爆炸

class Battleship {// 战舰类
    //战舰有宽 高 x坐标 y坐标 生命数量 移动速度
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int life;   //生命数量
    int speed;  //移动速度
	//左右移动
    void move(){}
    //发射深水炸弹
    void shootBombs(){}
}

我们设计几个敌方战舰:水雷艇,鱼雷艇,潜水艇
它们的攻击方式是发射: 水雷和深水炸弹
这些的初始代码其实都跟战舰相同,只是没有生命值,因为他只有一条命

下面是它们初始化代码

水雷艇特征和行为
public class MineSubmarine {
    //水雷艇有宽 高 x坐标 y坐标 移动速度
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
	//向右移动
    void move(){}
    //发射水雷
    void newMine(){}
}
侦查艇特征和行为
public class ObserveSubmarine {//侦查艇
    //侦查艇有宽 高 x坐标 y坐标 生命数量 移动速度
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
	//向右移动
    void move(){}
}
鱼雷艇特征和行为
public class TorpedoSubmarine {//鱼雷艇
    //鱼雷艇有宽 高 x坐标 y坐标 移动速度
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
	//向右移动
    void move(){}
}
水雷特征和行为
public class Mine {
    //水雷有宽 高 x坐标 y坐标 移动速度
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
	//向上移动
    void move(){}
}
深水炸弹特征和行为
public class Bomb {
    //深水炸弹有宽 高 x坐标 y坐标 移动速度
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
	//向下移动
    void move(){}
}

2. 归纳共有特征

它们的共有特征有宽,高,x坐标,y坐标,移动速度
所以我们新建一个父类,把共有特征和方法放进去,父类叫SeaObject。因为它们都是在海里运行的所以叫Sea Object(海洋物体)

共有的特征有: 宽, 高, x坐标, y 坐标,移动速度
共有的方法:
1.移动,因为每个类的移动行为都不一样,所以写成抽象模式,强制重写

abstract public class SeaObject {
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
    abstract void move();
}

因为我们把move,每个类的移动行为都不一样,所以写成抽象模式,让派生类强制重写

代码:

Battleship

package submarine;

public class Battleship extends SeaObject{
    int life;   //生命数量
    //左右移动
    void move(){}
    //发射深水炸弹
    void shootBombs(){}
}

Bomb

package submarine;

public class Bomb extends SeaObject{
    //向下移动
    void move(){}
}

Mine

package submarine;

public class Mine extends SeaObject {
    //向上移动
    void move(){}
}

Minesubmarine

package submarine;

public class MineSubmarine extends SeaObject{
    //向右移动
    void move(){}
    //发射水雷
    void shoot(){}
}

ObserveSubmarine

package submarine;

public class ObserveSubmarine extends SeaObject{
    //向右移动
    void move(){}
}

SeaObject

package submarine;

abstract public class SeaObject {
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
    abstract void move();
}

TorpedoSubmarine 鱼雷艇

package submarine;

public class TorpedoSubmarine extends SeaObject{
    //向右移动
    void move(){}
}

3.初始化

常量:

  1. 6个类的长,宽高都是固定的
  2. 窗口的长宽高都是固定的

变量

  1. 6个类的一开始坐标(x和y),注意左上角的坐标是(0,0)
  2. 速度

下面是各个数据

类名



初始坐标(x,y)

Battleship

66

26

(270.142)

Bomb

9

12

按空格键出来,这里不定义

mine

11

11

生成水雷艇后定时器自动生成

MineSubmarine

63

19

在窗口左边自动生成

TorpedoSubmarine

64

20

在窗口左边自动生成

ObserveSubmarine

63

19

在窗口左边自动生成

窗口

641

479


6个类的除了我们设bomb和mine我们设成3,其他类速度是随机数从1-3不等

这里我们新建一个World类专门写main,在加上一个窗口的初始化

这里给类初始值得方法叫做构造方法。注意这里bomb和mine之间的xy的值不是固定的,所以要把它们在超类里重新定义(重载一个构造方法,相同方法名 不同参数列表)
然后给他们加修饰符(访问权限),注意泛生类的访问权限大于或等于超类,一般加public即可

代码
Battleship战舰

package submarine;

public class Battleship extends SeaObject{
    //战舰的宽
    public static final int WIDTH = 66;
    //战舰的高
    public static final int HEIGHT = 26;
    //生命数量
    private int life;
    //初始化
    Battleship(){
        //把船放到海平面上的任意位置,x,y是左上角
        //初始位置是(200,150-Battleship.HEIGHT),速度是3
        super(Battleship.WIDTH,Battleship.HEIGHT,200,150-Battleship.HEIGHT,3);
        //初始命是5
        life = 5;
    }
    //左右移动
    public void move(){}
    //发射深水炸弹
    public void shootBombs(){}
}

Bomb炸弹

package submarine;

public class Bomb extends SeaObject{
    //炸弹的宽
    public static final int WIDTH = 9;
    //炸弹的高
    public static final int HEIGHT = 12;
    //深水炸弹初始化
    Bomb(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,3);
    }
    //向下移动
    public void move(){}

}

水雷Mine

package submarine;

public class Mine extends SeaObject {
    //水雷的宽
    public static final int WIDTH = 11;
    //水雷的高
    public static final int HEIGHT = 11;
    //水雷初始化
    Mine(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,3);
    }
    //向上移动
    public void move(){}
}

MineSubmarine 水雷艇

package submarine;

public class MineSubmarine extends SeaObject{
    //水雷艇的宽
    public static final int WIDTH = 63;
    //水雷艇的高
    public static final int HEIGHT = 19;
    //向右移动
    public void move(){}
    //水雷潜艇初始化
    MineSubmarine(){
        super(MineSubmarine.WIDTH,MineSubmarine.HEIGHT);
    }
    //发射水雷
    public void newMine(){}

}

ObserveSubmarine 侦查艇

package submarine;

public class ObserveSubmarine extends SeaObject{
    //侦查艇的宽
    public static final int WIDTH = 63;
    //侦查艇的高
    public static final int HEIGHT = 19;
    //侦查潜艇初始化
    ObserveSubmarine(){
        super(ObserveSubmarine.WIDTH,ObserveSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){}
}

Seaobject超类

package submarine;

import java.util.Random;

public abstract class SeaObject {
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
    public abstract void move();
    SeaObject(int width,int height,int x,int y,int speed ){//bomb还有mine
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
        this.speed = speed;
    }
    SeaObject(int width,int height){
        this.width = width;
        this.height = height;
        x = -width;//潜艇进场是在窗口最左边进场
        //海平面以下随机生成,随机高度在150到(最低减去一个潜艇的高度)
        Random rand = new Random();
        y = rand.nextInt(World.HEIGHT-World.SEA_LEVEL_HEIGHT-MineSubmarine.HEIGHT+1)+150;
        speed = rand.nextInt(3);
    }

}

TorpedoSubmarine 鱼雷艇

package submarine;

public class TorpedoSubmarine extends SeaObject{
    //鱼雷艇的宽
    public static final int WIDTH = 64;
    //鱼雷艇的高
    public static final int HEIGHT = 20;
    //水雷潜艇的初始化
    TorpedoSubmarine(){
        super(TorpedoSubmarine.WIDTH,TorpedoSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){}
}

World主方法

package submarine;

public class World {
    //海平面
    public static final int SEA_LEVEL_HEIGHT = 150;
    //窗口宽
    public static final int WIDTH = 641;
    //窗口长
    public static final int HEIGHT = 479;
    //主方法
    public static void main(String[] args) {

    }
}

4. 画图

这里我们用了现在不怎么用的方法,这里就不说了,直接是赋值黏贴过来的

  • 这里需要import Graphics,Jframe和JPanel三个类
  • main还要继承JFrame
  • 直接在main里面添加代码,复制黏贴就行
JFrame frame = new JPanel ();
World world = new World();
world.setFocusable(true);
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH+16, HEIGHT+39);
frame.setLocationRelativeTo(null);
frame.setVisible(true);//1)设置窗口可见  2)尽快调用paint()方法

我们需要写一个paint方法来画

  • 最主要把图片画出来的方法是paintIcon,有兴趣的自己了解即可,但现在没什么人用了。写了一个Image
  • 画的动作是一样的,就是对象图片调用paintIcon方法,所以把他们写在超类SeaObject里面
  • 把每个类都画一个在画布上
    Battleship
package submarine;

import javax.swing.*;

public class Battleship extends SeaObject{
    //战舰的宽
    public static final int WIDTH = 66;
    //战舰的高
    public static final int HEIGHT = 26;
    //生命数量
    private int life;
    //初始化
    Battleship(){
        //把船放到海平面上的任意位置,x,y是左上角
        //初始位置是(200,150-Battleship.HEIGHT),速度是3
        super(Battleship.WIDTH,Battleship.HEIGHT,200,150-Battleship.HEIGHT,3);
        //初始命是5
        life = 5;
    }
    //左右移动
    public void move(){}
    //发射深水炸弹
    public void shootBombs(){}
    //画布上画battleship对象
    public ImageIcon getObject(){
        return Images.battleship;
    }
}

Bomb

package submarine;

import javax.swing.*;

public class Bomb extends SeaObject{
    //炸弹的宽
    public static final int WIDTH = 9;
    //炸弹的高
    public static final int HEIGHT = 12;
    //深水炸弹初始化
    Bomb(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,3);
    }
    //向下移动
    public void move(){}
    //画布上画bomb对象
    public ImageIcon getObject(){
        return Images.bomb;
    }

}

Images

package submarine;
import javax.swing.ImageIcon;
/** 图片类 */
public class Images {
    //  公开的  静态的  图片数据类型   变量名
    public static ImageIcon  battleship; //战舰图片
    public static ImageIcon  obsersubm;  //侦察潜艇图片
    public static ImageIcon  torpesubm;  //鱼雷潜艇图片
    public static ImageIcon  minesubm;   //水雷潜艇图片
    public static ImageIcon  mine;       //水雷图片
    public static ImageIcon  bomb;       //深水炸弹图片
    public static ImageIcon  sea;        //海洋图
    public static ImageIcon  gameover;   //游戏结束图

    static{ //给静态图片赋值
        battleship = new ImageIcon("img/battleship.png");
        obsersubm = new ImageIcon("img/obsersubm.png");
        torpesubm = new ImageIcon("img/torpesubm.png");
        minesubm = new ImageIcon("img/minesubm.png");
        mine = new ImageIcon("img/mine.png");
        bomb = new ImageIcon("img/bomb.png");
        sea = new ImageIcon("img/sea.png");
        gameover = new ImageIcon("img/gameover.png");
    }

    public static void main(String[] args) {
    	//写完上面对象之后,测试,返回8表示读取成功
        System.out.println(battleship.getImageLoadStatus()); //返回8表示读取成功
        System.out.println(obsersubm.getImageLoadStatus());
        System.out.println(torpesubm.getImageLoadStatus());
        System.out.println(minesubm.getImageLoadStatus());
        System.out.println(mine.getImageLoadStatus());
        System.out.println(bomb.getImageLoadStatus());
        System.out.println(sea.getImageLoadStatus());
        System.out.println(gameover.getImageLoadStatus());
    }

}

Mine

package submarine;

import javax.swing.*;

public class Mine extends SeaObject {
    //水雷的宽
    public static final int WIDTH = 11;
    //水雷的高
    public static final int HEIGHT = 11;
    //水雷初始化
    Mine(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,3);
    }
    //向上移动
    public void move(){}
    //画布上画mine对象
    public ImageIcon getObject(){
        return Images.mine;
    }
}

MineSubmarine

package submarine;

import javax.swing.*;

public class MineSubmarine extends SeaObject{
    //水雷艇的宽
    public static final int WIDTH = 63;
    //水雷艇的高
    public static final int HEIGHT = 19;
    //向右移动
    public void move(){}
    //水雷潜艇初始化
    MineSubmarine(){
        super(MineSubmarine.WIDTH,MineSubmarine.HEIGHT);
    }
    //发射水雷
    public void newMine(){}
    //画布上画MineSubmarine对象
    public ImageIcon getObject(){
        return Images.minesubm;
    }
}

ObserveSubmarine

package submarine;

import javax.swing.*;

public class ObserveSubmarine extends SeaObject{
    //侦查艇的宽
    public static final int WIDTH = 63;
    //侦查艇的高
    public static final int HEIGHT = 19;
    //侦查潜艇初始化
    ObserveSubmarine(){
        super(ObserveSubmarine.WIDTH,ObserveSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){}
    //画布上画ObserveSubmarine对象
    public ImageIcon getObject(){
        return Images.obsersubm;
    }
}

SeaObject

package submarine;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.ImageIcon;
public abstract class SeaObject {
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
    public abstract void move();
    SeaObject(int width,int height,int x,int y,int speed ){//bomb还有mine
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
        this.speed = speed;
    }
    SeaObject(int width,int height){
        this.width = width;
        this.height = height;
        x = width;//潜艇进场是在窗口最左边进场
        //海平面以下随机生成,随机高度在150到(最低减去一个潜艇的高度)
        Random rand = new Random();
        y = rand.nextInt(World.HEIGHT-World.SEA_LEVEL_HEIGHT-MineSubmarine.HEIGHT+1)+150;
        speed = rand.nextInt(3);
    }
    //画布上画子类的对象,行为一样,所以定义为抽象
    public abstract ImageIcon getObject();
    //画对象 g:画笔
    public void paintImage(Graphics g){
        this.getObject().paintIcon(null,g,this.x,this.y);
    }

}

TorpedoSubmarine

package submarine;

import javax.swing.*;

public class TorpedoSubmarine extends SeaObject{
    //鱼雷艇的宽
    public static final int WIDTH = 64;
    //鱼雷艇的高
    public static final int HEIGHT = 20;
    //水雷潜艇的初始化
    TorpedoSubmarine(){
        super(TorpedoSubmarine.WIDTH,TorpedoSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){}
    //画布上画TorpedoSubmarine对象
    public ImageIcon getObject(){
        return Images.torpesubm;
    }
}

World

package submarine;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
public class World extends JPanel {
    //海平面
    public static final int SEA_LEVEL_HEIGHT = 150;
    //窗口宽
    public static final int WIDTH = 641;
    //窗口长
    public static final int HEIGHT = 479;
    //定义一个战舰对象
    Battleship ship = new Battleship();
    SeaObject[] submarines = {new MineSubmarine(),new ObserveSubmarine(),new TorpedoSubmarine()};
    Bomb[] bombs = {new Bomb(200,200)};
    Mine[] mines = {new Mine(200,150)};

    public void paint(Graphics g){
        //先画背景图
        Images.sea.paintIcon(null,g,0,0);
        //画战舰
        ship.paintImage(g);
        //画潜水艇数组
        for(int i = 0;i<submarines.length;i++){
            submarines[i].paintImage(g);
        }
        //画深水炸弹数组
        for(int i = 0;i<bombs.length;i++){
            bombs[i].paintImage(g);
        }
        //画水雷数组
        for(int i = 0;i<mines.length;i++){
            mines[i].paintImage(g);
        }

    }

    //主方法
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        World world = new World();
        world.setFocusable(true);
        frame.add(world);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH+16, HEIGHT+39);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);//1)设置窗口可见  2)尽快调用paint()方法
    }
}

代码呈现出来的是这样的

java实现短视频弹幕_初始化_02


如果你跟我一样再画布上呈现了6个类说明你已经成功一半了,剩下的就是让这些类移动起来

5. 重写方法

这里使用定时器每10毫秒重写画一次对象(导入Timer和TimerTask两个类)

重写move方法


移动方向

战舰移动

战舰由键盘左右控制移动

深水炸弹移动

由战舰发出,只能向下移动

潜水艇移动

潜水艇只能向右移动

水雷移动

水雷由水雷艇发射,向上移动

定时器

需要导入两个类一个是Timer,一个是TimerTask

import java.util.Timer;
import java.util.TimerTask;
public void action(){
    int period = 10;//delay和period都设置为10毫秒
    Timer timer = new Timer();//新建一个Timer对象
    timer.schedule(new TimerTask() {
        /**
         * 每10毫秒调用schedule
         * 并重写TimerTask里面的run方法
         * 这里用到的知识是匿名内部类
         */
        @Override
        public void run() {
            /**
             * 这里面需要写的是新建潜艇,深水炸弹,水雷对象
             * 移动
             * 撞击爆炸消失等方法
             */
            
            repaint();//重写调用paint方法
        }
    }, period, period);
}
新建潜水艇对象

这里用随机数的方法来生成潜艇

public SeaObject newSubmarine(){
    Random rand = new Random();//生成一个Random对象
    int number = rand.nextInt(3);//随机数是0到2
    if(number<1){//如果number等于0,输出水雷艇
        return new MineSubmarine();
    }else if(number<2){//如果number等于1,输出侦查艇
        return new ObserveSubmarine();
    }else{//如果number等于2,输出鱼雷艇
        return new TorpedoSubmarine();
    }
}
private int submarineIndex = 0;
public void addSubmarine(){
    submarineIndex++;
    if(submarineIndex%40 == 25){//每40*10=400毫秒=0.4秒生成潜艇
        //给submarines数组扩容
        submarines = Arrays.copyOf(submarines,submarines.length+1);
        SeaObject new_Submarine = newSubmarine();//新建潜水艇对象
        //扩容之后,把新的潜水艇对象保存在submarines数组里面
        submarines[submarines.length-1] = new_Submarine;
    }
}
新建水雷

在Worls中添加方法

private int mineIndex = 0;
public  void addMine(){
    /**
     * 水雷,因为是水雷潜水艇发射的,需要知道水雷潜水艇x和y的坐标
     * 所以写在MineSubmarine类里面
     * 我们先要分辨数组里面哪一个是MineSubmarine
     * 再给他添加水雷
     */
    mineIndex++;
    if(mineIndex%80==79){
        for(int i = 0; i <submarines.length;i++){
            if(submarines[i] instanceof MineSubmarine){
                /**
                 * 水雷是写在MineSubmarine类里面
                 * 为了调用MineSubmarine
                 * 我们需要强制转换submarines[i]成MineSubmarine类型
                 */
                //给水雷数组扩容
                mines = Arrays.copyOf(mines,mines.length+1);
                //强转的目的是现在潜水艇的数据类型是父类SeaObjext类型
                //但是我们要调用子类的方法,所以要强转
                //调用newMine生成新的水雷
                mines[mines.length-1] = ((MineSubmarine) submarines[i]).newMine();
            }
        }
    }
}
新建深水炸弹
  • 因为深水炸弹是在有人控制键盘,并在战舰中发射出去
  • 要先得到战舰的坐标,所以在战舰类里面写一个shootBombs方法,里面生成一个新的深水炸弹对象。
  • 最后在World类里面写一个addBomb方法调shootBombs方法,里面是按键盘触发相应的动作

战舰类里面的shootBombs方法

public  Bomb shootBombs(){
    return new Bomb(this.x-10,this.y);
}

World类里面的addBomb方法

public void addBomb(){
    KeyAdapter k = new KeyAdapter() { //不要求掌握
        /** 重写keyReleased()按键抬起事件 */
        public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发--不要求掌握
            if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键--不要求掌握
                Bomb obj = ship.shootBombs(); //获取深水炸弹对象
                bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
                bombs[bombs.length-1] = obj; //将obj添加到bombs的最后一个元素上
            }
            if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左键头--不要求掌握
                ship.moveLeft(); //战舰左移
            }
            if(e.getKeyCode()==KeyEvent.VK_RIGHT){ //若抬起的是右键头--不要求掌握
                ship.moveRight(); //战舰右移
            }
        }
    };
    this.addKeyListener(k); //不要求掌握
}
重写所有类的move方法

Battleship

public void moveLeft(){//键盘控制向左移动
    x-=7;
}
public void moveRight(){//键盘控制向右移动
    x+=7;
}

Bomb

//向下移动
public void move(){
    y+=speed;
}

Mine

//向上移动
public void move(){
    y-=speed;
}

MineSubmarine

//向右移动
public void move(){
    x+=speed;
}

ObserveSubmarine

//向右移动
public void move(){
    x+=speed;
}

TorpedoSubmarine

//向右移动
public void move(){
    x+=speed;
}

代码
记得数组初始化的时候清空里面的元素,之前是测试版

Battleship

package submarine;

import javax.swing.*;

public class Battleship extends SeaObject{
    //战舰的宽
    public static final int WIDTH = 66;
    //战舰的高
    public static final int HEIGHT = 26;
    //生命数量
    private int life;
    //初始化
    Battleship(){
        //把船放到海平面上的任意位置,x,y是左上角
        //初始位置是(200,150-Battleship.HEIGHT),速度是3
        super(Battleship.WIDTH,Battleship.HEIGHT,200,150-Battleship.HEIGHT,3);
        //初始命是5
        life = 5;
    }
    //左右移动
    public void move(){}
    //发射深水炸弹
    public  Bomb shootBombs(){
        return new Bomb(this.x-10,this.y);
    }
    //画布上画battleship对象
    public ImageIcon getObject(){
        return Images.battleship;
    }
    public void moveLeft(){//键盘控制向左移动
        this.x-=7;
    }
    public void moveRight(){//键盘控制向右移动
        this.x+=7;
    }

}

Bomb

package submarine;

import javax.swing.*;

public class Bomb extends SeaObject{
    //炸弹的宽
    public static final int WIDTH = 9;
    //炸弹的高
    public static final int HEIGHT = 12;
    //深水炸弹初始化
    Bomb(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,3);
    }
    //向下移动
    public void move(){
        y+=speed;
    }
    //画布上画bomb对象
    public ImageIcon getObject(){
        return Images.bomb;
    }

}

Images

package submarine;

import javax.swing.*;

public class Bomb extends SeaObject{
    //炸弹的宽
    public static final int WIDTH = 9;
    //炸弹的高
    public static final int HEIGHT = 12;
    //深水炸弹初始化
    Bomb(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,3);
    }
    //向下移动
    public void move(){
        y+=speed;
    }
    //画布上画bomb对象
    public ImageIcon getObject(){
        return Images.bomb;
    }

}

Mine

package submarine;

import javax.swing.*;

public class Mine extends SeaObject {
    //水雷的宽
    public static final int WIDTH = 11;
    //水雷的高
    public static final int HEIGHT = 11;
    //水雷初始化
    Mine(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,2);
    }
    //向上移动
    public void move(){
        y-=speed;
    }
    //画布上画mine对象
    public ImageIcon getObject(){
        return Images.mine;
    }
}

MineSubmarine

package submarine;

import javax.swing.*;

public class MineSubmarine extends SeaObject{
    //水雷艇的宽
    public static final int WIDTH = 63;
    //水雷艇的高
    public static final int HEIGHT = 19;
    //向右移动
    public void move(){
        x+=speed;
    }
    //水雷潜艇初始化
    MineSubmarine(){
        super(MineSubmarine.WIDTH,MineSubmarine.HEIGHT);
    }
    //发射水雷
    public Mine newMine(){//生成鱼雷
        //可以控制水雷出现在水雷潜艇的哪一边
        //这里用的是出现在水雷潜艇中间偏上
        return new Mine(this.x+30,this.y-5);
    }
    //画布上画MineSubmarine对象
    public ImageIcon getObject(){
        return Images.minesubm;
    }
}

ObserveSubmarine

package submarine;

import javax.swing.*;

public class ObserveSubmarine extends SeaObject{
    //侦查艇的宽
    public static final int WIDTH = 63;
    //侦查艇的高
    public static final int HEIGHT = 19;
    //侦查潜艇初始化
    ObserveSubmarine(){
        super(ObserveSubmarine.WIDTH,ObserveSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){
        x+=speed;
    }
    //画布上画ObserveSubmarine 对象
    public ImageIcon getObject(){
        return Images.obsersubm;
    }
}

SeaObject

package submarine;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.ImageIcon;
public abstract class SeaObject {
    int width;  //宽
    int height; //高
    int x;      //x坐标
    int y;      //y坐标
    int speed;  //移动速度
    public abstract void move();
    SeaObject(int width,int height,int x,int y,int speed ){//bomb还有mine
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
        this.speed = speed;
    }
    SeaObject(int width,int height){
        this.width = width;
        this.height = height;
        x = -width;//潜艇进场是在窗口最左边进场
        //海平面以下随机生成,随机高度在150到(最低减去一个潜艇的高度)
        Random rand = new Random();
        y = rand.nextInt(World.HEIGHT-World.SEA_LEVEL_HEIGHT-MineSubmarine.HEIGHT+1)+150;
        speed = rand.nextInt(3);
    }
    //画布上画子类的对象,行为一样,所以定义为抽象
    public abstract ImageIcon getObject();
    //画对象 g:画笔
    public void paintImage(Graphics g){
        this.getObject().paintIcon(null,g,this.x,this.y);
    }
}

TorpedoSubmarine

package submarine;

import javax.swing.*;

public class TorpedoSubmarine extends SeaObject{
    //鱼雷艇的宽
    public static final int WIDTH = 64;
    //鱼雷艇的高
    public static final int HEIGHT = 20;
    //水雷潜艇的初始化
    TorpedoSubmarine(){
        super(TorpedoSubmarine.WIDTH,TorpedoSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){
        x+=speed;
    }
    //画布上画TorpedoSubmarine对象
    public ImageIcon getObject(){
        return Images.torpesubm;
    }
}

World

package submarine;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
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 static final int SEA_LEVEL_HEIGHT = 150;
    //窗口宽
    public static final int WIDTH = 641;
    //窗口长
    public static final int HEIGHT = 479;
    //定义一个战舰对象
    Battleship ship = new Battleship();
    SeaObject[] submarines = {new MineSubmarine(),new ObserveSubmarine(),new TorpedoSubmarine()};
    Bomb[] bombs = {};
    Mine[] mines = {};

    public void paint(Graphics g){
        //先画背景图
        Images.sea.paintIcon(null,g,0,0);
        //画战舰
        ship.paintImage(g);
        //画潜水艇数组
        for(int i = 0;i<submarines.length;i++){
            submarines[i].paintImage(g);
        }
        //画深水炸弹数组
        for(int i = 0;i<bombs.length;i++){
            bombs[i].paintImage(g);
        }
        //画水雷数组
        for(int i = 0;i<mines.length;i++){
            mines[i].paintImage(g);
        }

    }
    public SeaObject newSubmarine(){
        Random rand = new Random();//生成一个Random对象
        int number = rand.nextInt(3);//随机数是0到2
        if(number<1){//如果number等于0,输出水雷艇
            return new MineSubmarine();
        }else if(number<2){//如果number等于1,输出侦查艇
            return new ObserveSubmarine();
        }else{//如果number等于2,输出鱼雷艇
            return new TorpedoSubmarine();
        }
    }
    private int submarineIndex = 0;
    public void addSubmarine(){
        submarineIndex++;
        if(submarineIndex%40 == 25){//每40*10=400毫秒=0.4秒生成潜艇
            //给submarines数组扩容
            submarines = Arrays.copyOf(submarines,submarines.length+1);
            SeaObject new_Submarine = newSubmarine();//新建潜水艇对象
            //扩容之后,把新的潜水艇对象保存在submarines数组里面
            submarines[submarines.length-1] = new_Submarine;
        }
    }
    private int mineIndex = 0;
    public  void addMine(){
        /**
         * 水雷,因为是水雷潜水艇发射的,需要知道水雷潜水艇x和y的坐标
         * 所以写在MineSubmarine类里面
         * 我们先要分辨数组里面哪一个是MineSubmarine
         * 再给他添加水雷
         */
        mineIndex++;
        if(mineIndex%80==79){
            for(int i = 0; i <submarines.length;i++){
                if(submarines[i] instanceof MineSubmarine){
                    /**
                     * 水雷是写在MineSubmarine类里面
                     * 为了调用MineSubmarine
                     * 我们需要强制转换submarines[i]成MineSubmarine类型
                     */
                    //给水雷数组扩容
                    mines = Arrays.copyOf(mines,mines.length+1);
                    //强转的目的是现在潜水艇的数据类型是父类SeaObjext类型
                    //但是我们要调用子类的方法,所以要强转
                    //调用newMine生成新的水雷
                    mines[mines.length-1] = ((MineSubmarine) submarines[i]).newMine();
                }
            }
        }
    }
    public void addBomb(){
        KeyAdapter k = new KeyAdapter() { //不要求掌握
            /** 重写keyReleased()按键抬起事件 */
            public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发--不要求掌握
                if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键--不要求掌握
                    Bomb obj = ship.shootBombs(); //获取深水炸弹对象
                    bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
                    bombs[bombs.length-1] = obj; //将obj添加到bombs的最后一个元素上
                }
                if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左键头
                    ship.moveLeft(); //战舰左移
                }
                if(e.getKeyCode()==KeyEvent.VK_RIGHT){ //若抬起的是右键头
                    ship.moveRight(); //战舰右移
                }
            }
        };
        this.addKeyListener(k); //不要求掌握
    }
    public void objectMove(){
        /**
         * 遍历潜水艇,水雷,深水炸弹让它们移动
         * Battleship是由键盘控制的
         */
        for(int i = 0;i<submarines.length;i++){
            submarines[i].move();
        }
        for(int i = 0;i<mines.length;i++){
            mines[i].move();
        }
        for(int i = 0;i<bombs.length;i++){
            bombs[i].move();
        }
    }
    public void action(){
        addBomb();
        int period = 10;//delay和period都设置为10毫秒
        Timer timer = new Timer();//新建一个Timer对象
        timer.schedule(new TimerTask() {
            /**
             * 每10毫秒调用schedule
             * 并重写TimerTask里面的run方法
             * 这里用到的知识是匿名内部类
             */
            @Override
            public void run() {
                /**
                 * 这里面需要写的是新建潜艇,深水炸弹,鱼雷对象
                 * 移动
                 * 撞击爆炸消失等方法
                 */
                addSubmarine();//生成新的潜水艇对象
                addMine();//生成新的深水炸弹对象

                objectMove();
                repaint();//重写调用paint方法
            }
        }, period, period);
    }
    //主方法
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        World world = new World();
        world.setFocusable(true);
        frame.add(world);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH+16, HEIGHT+39);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);//1)设置窗口可见  2)尽快调用paint()方法
        world.action();
    }
}

6. 把超过窗口和相互碰撞的物体删掉

删除超过窗口的对象
public void deleteOutOfBounds(){//删除超过边界的对象
    //潜水艇超过边界会被删除
    for(int i = 0;i<submarines.length;i++){
        //潜水艇只有向右的动作,所以当有潜水艇超过右边边界,就会被删除
        if(submarines[i].x>=World.WIDTH){
            //思路是让要删除的这个数组元素下标等于,最后一个数组元素,再缩容
            submarines[i] = submarines[submarines.length-1];
            submarines = Arrays.copyOf(submarines,submarines.length-1);
        }
    }
    //水雷超过边界会被删除
    for(int i = 0;i<mines.length;i++){
        //水雷只有向上的动作,所以当有水雷超过海平面,就会被删除
        if(mines[i].y<World.SEA_LEVEL_HEIGHT){
            //思路是让要删除的这个数组元素下标等于,最后一个数组元素,再缩容
            mines[i] = mines[mines.length-1];
            mines = Arrays.copyOf(mines,mines.length-1);
        }
    }
    //深水炸弹超过边界会被删除
    for(int i = 0;i<bombs.length;i++){
        //深水炸弹只有向下的动作,所以当有深水炸弹超过最下面边界,就会被删除
        if(bombs[i].y>World.HEIGHT){
            //思路是让要删除的这个数组元素下标等于,最后一个数组元素,再缩容
            bombs[i] = bombs[bombs.length-1];
            bombs = Arrays.copyOf(bombs,bombs.length-1);
        }
    }
}
删除相互碰撞的物体
  • 这里要判断两个物体是否碰撞就可以定义一个为参照物,其他对象在哪个范围就代表着碰撞
  • 战舰和水雷碰撞,深水炸弹和潜水艇碰撞
  • 碰撞的结果:战舰和水雷碰撞,水雷消失,战舰命减一,如果命为零,游戏结束
  • 深水炸弹和潜水艇碰撞两个都消失,战舰命加一
  • 方法是写在SeaObject里面,返回值是boolean类型

因为坐标出现在每个物体的左上角,所以以other作为参照物,只需要计算黄色部分范围即可

public boolean isHit(SeaObject other){
   return other.x>=this.x-other.width && other.x<=this.x+this.width &&
            other.y>=this.y-other.height && other.y<=this.y+this.height;
}

在Battleship类里面写一个得到生命和失去生命的方法,因为在Battleship中life是私有的,所以用方法输出

public int getLife(){
    return life;
}
public void subtractLife(){
    life--;
}

在paint里面加两行,分别对应的是在画布上画出score和life来

g.drawString("SCORE: "+score,200,50); //画分
g.drawString("LIFE: "+ship.getLife(),400,50); //画命
  • 我们设定只有水雷潜艇是得命,其他潜艇得分
  • 为了区分它们,我们写两个接口让它们继承,这样如果判断潜艇和深水炸弹是碰撞的,那我们就判断是不是属于命或者得分接口
  • 最后写了判断有没有碰撞的方法在World里面
public void judgeIsHit(){
    //判断潜水艇和深水炸弹碰撞
    for(int i = 0;i<submarines.length;i++){
        for(int j = 0;j<bombs.length;j++){
            if(submarines[i].isHit(bombs[j])){
                //把潜水艇和深水炸弹的状态设为DEAD
                submarines[i].status = SeaObject.DEAD;
                bombs[j].status = SeaObject.DEAD;
                //得分 先判断这个潜艇对象是否继承EnemyScore接口
                if(submarines[i] instanceof EnemyScore){
                    score += ((EnemyScore) submarines[i]).getScore();
                }
                //得命 先判断这个潜艇对象是否继承EnemyLife接口
                if(submarines[i] instanceof EnemyLife){
                    ship.addLife();//加命
                }
            }
        }
    }
    //判断战舰和水雷碰撞
        for(int i = 0;i<mines.length;i++){
            System.out.println("ship"+(ship.y+ship.height));
            System.out.println("mines"+i+"   "+mines[i].y);
            if(mines[i].isHit(ship)){
                //把水雷的状态设为DEAD
                mines[i].status = SeaObject.DEAD;
                ship.subtractLife();
            }
        }
}

最后判断当命为0的时候 ,游戏结束

  • 定义两个常量
  • 写一个判断游戏状态的方法在World里面
public void checkGameOverAction(){
    if(ship.getLife()==0){
        gameStatus = GAMEOVER;
    }
}

最后改paint方法,当gameStatus = GAMEOVER;游戏就结束,当gameStatus = RUNNING;的时候,游戏没结束

7.最终代码

Battleship
package submarine;

import javax.swing.*;

public class Battleship extends SeaObject{
    //战舰的宽
    public static final int WIDTH = 66;
    //战舰的高
    public static final int HEIGHT = 26;
    //生命数量
    private int life;
    //初始化
    Battleship(){
        //把船放到海平面上的任意位置,x,y是左上角
        //初始位置是(200,150-Battleship.HEIGHT),速度是3
        super(Battleship.WIDTH,Battleship.HEIGHT,200,150-Battleship.HEIGHT,3);
        //初始命是5
        life = 5;
    }
    //左右移动
    public void move(){}
    //发射深水炸弹
    public  Bomb shootBombs(){
        return new Bomb(this.x-10,this.y);
    }
    //画布上画battleship对象
    public ImageIcon getObject(){
        return Images.battleship;
    }
    public void moveLeft(){//键盘控制向左移动
        this.x-=7;
    }
    public void moveRight(){//键盘控制向右移动
        this.x+=7;
    }
    public int getLife(){
        return life;
    }
    public void addLife(){
        life++;
    }
    public void subtractLife(){
        life--;
    }
}
Bomb
package submarine;

import javax.swing.*;

public class Bomb extends SeaObject{
    //炸弹的宽
    public static final int WIDTH = 9;
    //炸弹的高
    public static final int HEIGHT = 12;
    //深水炸弹初始化
    Bomb(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,3);
    }
    //向下移动
    public void move(){
        y+=speed;
    }
    //画布上画bomb对象
    public ImageIcon getObject(){
        return Images.bomb;
    }

}

EnemyLife

package submarine;

public interface EnemyLife {
    public int getLife();
}

EnemyScore

package submarine;

public interface EnemyScore {
    public int getScore();
}
Images
package submarine;


import javax.swing.ImageIcon;
/** 图片类 */
public class Images {
    //  公开的  静态的  图片数据类型   变量名
    public static ImageIcon  battleship; //战舰图片
    public static ImageIcon  obsersubm;  //侦察潜艇图片
    public static ImageIcon  torpesubm;  //鱼雷潜艇图片
    public static ImageIcon  minesubm;   //水雷潜艇图片
    public static ImageIcon  mine;       //水雷图片
    public static ImageIcon  bomb;       //深水炸弹图片
    public static ImageIcon  sea;        //海洋图
    public static ImageIcon  gameover;   //游戏结束图

    static{ //给静态图片赋值
        battleship = new ImageIcon("img/battleship.png");
        obsersubm = new ImageIcon("img/obsersubm.png");
        torpesubm = new ImageIcon("img/torpesubm.png");
        minesubm = new ImageIcon("img/minesubm.png");
        mine = new ImageIcon("img/mine.png");
        bomb = new ImageIcon("img/bomb.png");
        sea = new ImageIcon("img/sea.png");
        gameover = new ImageIcon("img/gameover.png");
    }

    public static void main(String[] args) {
        //写完上面对象之后,测试,返回8表示读取成功
        System.out.println(battleship.getImageLoadStatus()); //返回8表示读取成功
        System.out.println(obsersubm.getImageLoadStatus());
        System.out.println(torpesubm.getImageLoadStatus());
        System.out.println(minesubm.getImageLoadStatus());
        System.out.println(mine.getImageLoadStatus());
        System.out.println(bomb.getImageLoadStatus());
        System.out.println(sea.getImageLoadStatus());
        System.out.println(gameover.getImageLoadStatus());
    }

}
Mine
package submarine;

import javax.swing.*;

public class Mine extends SeaObject {
    //水雷的宽
    public static final int WIDTH = 11;
    //水雷的高
    public static final int HEIGHT = 11;
    //水雷初始化
    Mine(int x,int y){
        super(Bomb.WIDTH,Bomb.HEIGHT,x,y,1);
    }
    //向上移动
    public void move(){
        y-=speed;
    }
    //画布上画mine对象
    public ImageIcon getObject(){
        return Images.mine;
    }
}
MineSubmarine
package submarine;

import javax.swing.*;

public class MineSubmarine extends SeaObject implements EnemyLife{
    //水雷艇的宽
    public static final int WIDTH = 63;
    //水雷艇的高
    public static final int HEIGHT = 19;
    //向右移动
    public void move(){
        x+=speed;
    }
    //水雷潜艇初始化
    MineSubmarine(){
        super(MineSubmarine.WIDTH,MineSubmarine.HEIGHT);
    }
    //发射水雷
    public Mine newMine(){//生成鱼雷
        //可以控制水雷出现在水雷潜艇的哪一边
        //这里用的是出现在水雷潜艇中间偏上
        return new Mine(this.x+30,this.y-5);
    }
    //画布上画MineSubmarine对象
    public ImageIcon getObject(){
        return Images.minesubm;
    }

    public int getLife(){
        return 1; //打掉水雷潜艇,战舰得1条命
    }
}
ObserveSubmarine
package submarine;

import javax.swing.*;

public class ObserveSubmarine extends SeaObject  implements EnemyScore{
    //侦查艇的宽
    public static final int WIDTH = 63;
    //侦查艇的高
    public static final int HEIGHT = 19;
    //侦查潜艇初始化
    ObserveSubmarine(){
        super(ObserveSubmarine.WIDTH,ObserveSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){
        x+=speed;
    }
    //画布上画ObserveSubmarine 对象
    public ImageIcon getObject(){
        return Images.obsersubm;
    }
    public int getScore(){
        return 20;
    }
}
SeaObject
package submarine;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.ImageIcon;
public abstract class SeaObject{
    public static final int DEAD = 0;
    public static final int LIVE = 1;
    public int status = LIVE;
    protected int width;  //宽
    protected int height; //高
    public int x;      //x坐标
    public int y;      //y坐标
    public int speed;  //移动速度

    public abstract void move();
    SeaObject(int width,int height,int x,int y,int speed ){//bomb还有mine
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
        this.speed = speed;
    }
    SeaObject(int width,int height){
        this.width = width;
        this.height = height;
        x = -width;//潜艇进场是在窗口最左边进场
        //海平面以下随机生成,随机高度在150到(最低减去一个潜艇的高度)
        Random rand = new Random();
        y = rand.nextInt(World.HEIGHT-World.SEA_LEVEL_HEIGHT-MineSubmarine.HEIGHT+1)+150;
        speed = rand.nextInt(3);
    }
    //画布上画子类的对象,行为一样,所以定义为抽象
    public abstract ImageIcon getObject();
    //画对象 g:画笔
    public void paintImage(Graphics g){
        this.getObject().paintIcon(null,g,this.x,this.y);
    }
    public boolean isHit(SeaObject other){
       return other.x>=this.x-other.width && other.x<=this.x+this.width &&
                other.y>=this.y-other.height && other.y<=this.y+this.height;
    }

}
TorpedoSubmarine
package submarine;

import javax.swing.*;

public class TorpedoSubmarine extends SeaObject implements EnemyScore{
    //鱼雷艇的宽
    public static final int WIDTH = 64;
    //鱼雷艇的高
    public static final int HEIGHT = 20;
    //水雷潜艇的初始化
    TorpedoSubmarine(){
        super(TorpedoSubmarine.WIDTH,TorpedoSubmarine.HEIGHT);
    }
    //向右移动
    public void move(){
        x+=speed;
    }
    //画布上画TorpedoSubmarine对象
    public ImageIcon getObject(){
        return Images.torpesubm;
    }
    public int getScore(){
        return 20;
    }
}
World
package submarine;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
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 static final int RUNNING = 1;
    //海平面
    public static final int GAMEOVER = 0;
    //窗口宽
    public static final int WIDTH = 641;
    //窗口长
    public static final int HEIGHT = 479;
    //海平面
    public static final int SEA_LEVEL_HEIGHT = 150;
    //分数
    public int score = 0;
    //定义一个战舰对象
    Battleship ship = new Battleship();
    SeaObject[] submarines = {};
    Bomb[] bombs = {};
    Mine[] mines = {};
    //游戏状态,RUNNING就是游戏没有结束,GAMEOVER就是游戏结束
    public int gameStatus = RUNNING;
    public int numberLife = 0;
    public void paint(Graphics g){
        if(gameStatus==GAMEOVER){
            if(numberLife==0){//让画布上的life变成0
                g.drawString("LIFE: "+ship.getLife(),400,50); //画命
                numberLife=1;
            }
            Images.gameover.paintIcon(null,g,0,0);
        }else if(gameStatus==RUNNING){
            //先画背景图
            Images.sea.paintIcon(null,g,0,0);
            //画战舰
            ship.paintImage(g);
            //画潜水艇数组
            for(int i = 0;i<submarines.length;i++){
                submarines[i].paintImage(g);
            }
            //画深水炸弹数组
            for(int i = 0;i<bombs.length;i++){
                bombs[i].paintImage(g);
            }
            //画水雷数组
            for(int i = 0;i<mines.length;i++){
                mines[i].paintImage(g);
            }
            g.drawString("SCORE: "+score,200,50); //画分
            g.drawString("LIFE: "+ship.getLife(),400,50); //画命
        }


    }
    public SeaObject newSubmarine(){
        Random rand = new Random();//生成一个Random对象
        int number = rand.nextInt(3);//随机数是0到2
        if(number<1){//如果number等于0,输出水雷艇
            return new MineSubmarine();
        }else if(number<2){//如果number等于1,输出侦查艇
            return new ObserveSubmarine();
        }else{//如果number等于2,输出鱼雷艇
            return new TorpedoSubmarine();
        }
    }
    private int submarineIndex = 0;
    public void addSubmarine(){
        submarineIndex++;
        if(submarineIndex%40 == 25){//每40*10=400毫秒=0.4秒生成潜艇
            //给submarines数组扩容
            submarines = Arrays.copyOf(submarines,submarines.length+1);
            SeaObject new_Submarine = newSubmarine();//新建潜水艇对象
            //扩容之后,把新的潜水艇对象保存在submarines数组里面
            submarines[submarines.length-1] = new_Submarine;
        }
    }
    private int mineIndex = 0;
    public  void addMine(){
        /**
         * 水雷,因为是水雷潜水艇发射的,需要知道水雷潜水艇x和y的坐标
         * 所以写在MineSubmarine类里面
         * 我们先要分辨数组里面哪一个是MineSubmarine
         * 再给他添加水雷
         */
        mineIndex++;
        if(mineIndex%80==79){
            for(int i = 0; i <submarines.length;i++){
                if(submarines[i] instanceof MineSubmarine){
                    /**
                     * 水雷是写在MineSubmarine类里面
                     * 为了调用MineSubmarine
                     * 我们需要强制转换submarines[i]成MineSubmarine类型
                     */
                    //给水雷数组扩容
                    mines = Arrays.copyOf(mines,mines.length+1);
                    //强转的目的是现在潜水艇的数据类型是父类SeaObjext类型
                    //但是我们要调用子类的方法,所以要强转
                    //调用newMine生成新的水雷
                    mines[mines.length-1] = ((MineSubmarine) submarines[i]).newMine();
                }
            }
        }
    }
    public void addBomb(){
        KeyAdapter k = new KeyAdapter() { //不要求掌握
            /** 重写keyReleased()按键抬起事件 */
            public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发
                if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键
                        Bomb obj = ship.shootBombs(); //获取深水炸弹对象
                        bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
                        bombs[bombs.length-1] = obj; //将obj添加到bombs的最后一个元素上
                }
                if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左键头--不要求掌握
                    ship.moveLeft(); //战舰左移
                }
                if(e.getKeyCode()==KeyEvent.VK_RIGHT){ //若抬起的是右键头--不要求掌握
                    ship.moveRight(); //战舰右移
                }
            }
        };
        this.addKeyListener(k); //不要求掌握
    }
    public void objectMove(){
        /**
         * 遍历潜水艇,水雷,深水炸弹让它们移动
         * Battleship是由键盘控制的
         */
        for(int i = 0;i<submarines.length;i++){
            submarines[i].move();
        }
        for(int i = 0;i<mines.length;i++){
            mines[i].move();
        }
        for(int i = 0;i<bombs.length;i++){
            bombs[i].move();
        }
    }
    public void deleteOutOfBounds(){//删除超过边界的对象
        //潜水艇超过边界会被删除
        for(int i = 0;i<submarines.length;i++){
            //潜水艇只有向右的动作,所以当有潜水艇超过右边边界,就会被删除
            if(submarines[i].x>=World.WIDTH||submarines[i].status==SeaObject.DEAD){
                //思路是让要删除的这个数组元素下标等于,最后一个数组元素,再缩容
                submarines[i] = submarines[submarines.length-1];
                submarines = Arrays.copyOf(submarines,submarines.length-1);
            }
        }
        //水雷超过边界会被删除
        for(int i = 0;i<mines.length;i++){
            //水雷只有向上的动作,所以当有水雷超过海平面,就会被删除
            if(mines[i].y<World.SEA_LEVEL_HEIGHT||mines[i].status==SeaObject.DEAD){
                //思路是让要删除的这个数组元素下标等于,最后一个数组元素,再缩容
                mines[i] = mines[mines.length-1];
                mines = Arrays.copyOf(mines,mines.length-1);
            }
        }
        //深水炸弹超过边界会被删除
        for(int i = 0;i<bombs.length;i++){
            //深水炸弹只有向下的动作,所以当有深水炸弹超过最下面边界,就会被删除
            if(bombs[i].y>World.HEIGHT||bombs[i].status==SeaObject.DEAD){
                //思路是让要删除的这个数组元素下标等于,最后一个数组元素,再缩容
                bombs[i] = bombs[bombs.length-1];
                bombs = Arrays.copyOf(bombs,bombs.length-1);
            }
        }
    }
    public void judgeIsHit(){
        //判断潜水艇和深水炸弹碰撞
        for(int i = 0;i<submarines.length;i++){
            for(int j = 0;j<bombs.length;j++){
                if(submarines[i].isHit(bombs[j])){
                    //把潜水艇和深水炸弹的状态设为DEAD
                    submarines[i].status = SeaObject.DEAD;
                    bombs[j].status = SeaObject.DEAD;
                    //得分 先判断这个潜艇对象是否继承EnemyScore接口
                    if(submarines[i] instanceof EnemyScore){
                        score += ((EnemyScore) submarines[i]).getScore();
                    }
                    //得命 先判断这个潜艇对象是否继承EnemyLife接口
                    if(submarines[i] instanceof EnemyLife){
                        ship.addLife();//加命
                    }
                }
            }
        }
        //判断战舰和水雷碰撞
            for(int i = 0;i<mines.length;i++){
                if(mines[i].isHit(ship)){
                    //把水雷的状态设为DEAD
                    mines[i].status = SeaObject.DEAD;
                    ship.subtractLife();
                }
            }
    }
    public void checkGameOverAction(){
        if(ship.getLife()==0){

            gameStatus = GAMEOVER;
        }
    }
    public void action(){
        addBomb();
        int period = 10;//delay和period都设置为10毫秒
        Timer timer = new Timer();//新建一个Timer对象
        timer.schedule(new TimerTask() {
            /**
             * 每10毫秒调用schedule
             * 并重写TimerTask里面的run方法
             * 这里用到的知识是匿名内部类
             */
            @Override
            public void run() {
                /**
                 * 这里面需要写的是新建潜艇,深水炸弹,鱼雷对象
                 * 移动
                 * 撞击爆炸消失等方法
                 */
                addSubmarine();//生成新的潜水艇对象
                addMine();//生成新的深水炸弹对象
                objectMove();//除了战舰所有对象移动
                deleteOutOfBounds();//删除超过边界的对象
                judgeIsHit();//判断两个对象是否碰撞
                checkGameOverAction();//检查游戏是不是结束
                repaint();//重写调用paint方法
            }
        }, period, period);
    }
    //主方法
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        World world = new World();
        world.setFocusable(true);
        frame.add(world);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH+16, HEIGHT+39);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);//1)设置窗口可见  2)尽快调用paint()方法
        world.action();
    }
}