Java新手小白入门篇 项目 – 深海杀手

导读:本篇文章讲解 Java新手小白入门篇 项目 – 深海杀手,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com


潜艇游戏需求:

  1. 所参与的角色:
    • 战舰、深水炸弹、侦察潜艇、鱼雷潜艇、水雷潜艇、水雷
  2. 角色间的关系:
    • 战舰发射深水炸弹
    • 深水炸弹可以打潜艇(侦察潜艇、鱼雷潜艇、水雷潜艇),若打中:
      • 潜艇消失、深水炸弹消失
      • 得东西:
        • 打掉侦察潜艇,玩家得10分
        • 打掉鱼雷潜艇,玩家得40分
        • 打掉水雷潜艇,战舰得1条命
    • 水雷潜艇可以发射水雷
    • 水雷可以击打战舰,若击中:
      • 水雷消失
      • 战舰减1条命(命数为0时游戏结束)

一、day01

  • 创建6个类,创建World类并测试

二、day02

  • 给6个类设计构造方法,并测试

三、day03

  • 设计侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组、水雷数组、炸弹数组,并测试
  • 设计SeaObject超类,设计6个类继承SeaObject
  • 给SeaObject设计两个构造方法,6个类中分别调用
  • 将侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组统一组合为SeaObject数组,并测试

四、day04

  • 给6个类重写 move() 使其坐标点移动,并测试

    • 侦察潜艇,鱼雷潜艇,水雷潜艇
      • 由于潜艇是从左往右移动,速度为speed,y 坐标不改变,改变的为 x 坐标
      • 方法实现为 x+=speed
    • 水雷
      • 由于水雷是从下往上移动,速度为speed,x 坐标不改变,改变的为 y 坐标
      • 方法实现为 y-=speed
    • 炸弹
      • 由于炸弹是从上往下移动,速度为speed,x 坐标不改变,改变的为 y 坐标
      • 方法实现为 y+=speed
    • 战舰
      • 由于它的移动方式和其他的不一致,方法只需要重写,具体实现先搁置不写
    • 测试
      • World
        • main方法
          • 分别创建 5 个对象

          • 分别输出每个对象的 x,y,speed 数值

          • 分别调用每个对象的 move() 方法

          • 在动用每个对象的 move() 方法后,再次对对象的 x,y,speed 值进行输出

          • 测试可参考以下示例:

             ObserveSubmarine o1 = new ObserveSubmarine();
                    System.out.println("侦察潜艇初始数据-----x:"+o1.x+",y:"+o1.y+",speed:"+o1.speed);
                    o1.move();
                    System.out.println("侦察潜艇移动后数据---x:"+o1.x+",y:"+o1.y+",speed:"+o1.speed);在这里插入代码片
            
  • 给类中成员添加访问控制修饰符

    • 访问控制修饰符是为了控制访问权限的,适当的减小访问权限可以使得代码更加安全
    • 通常我们会将属性(成员变量)私有化private,行为(方法)公开化public
    • 但是由于私有的属性不能被继承,这里我们还需要将 SeaObject 中的属性更改为 protected
  • 将图片拷贝到项目中

    • 在项目下创建 img 文件夹/目录
    • 将8张图片拷贝到 img 文件夹中
    • 最好将图片拷贝之后,进行一个 Build -> Rebuild Project 操作
      在这里插入图片描述
  • 设计Images图片类

    • 在项目的包下创建类 Images
    • 目的:
      • 这个类是为了封装我们的 图片对象而设计的
      • 思路:
        • 由于我们为了减少内存消耗,想让图片只有一份,这里使用 static 关键字对图片对象进行修饰
        • 由于我们想项目一起动就对图片进行加载,使用 static 关键字修饰静态代码块,为图片对象进行赋值
      • 创建8个图片对象,
        • public static ImageIcon xxx;
      • 静态代码块对图片对象进行赋值操作
        • static{xxx = new ImageIcon("img/xxx.png");}
    • 测试
      • Images 类中创建main方法
        • 在main方法中分别调用 System.out.println(xxx.getImageLoadStatus()); 进行测试
        • 测试结果为 8 ,表示正确

五、day05

今日目标
在这里插入图片描述

  • 设计窗口的宽和高为常量,在适当的地方做修改

    • World 类中设计宽WIDTH = 641,高HEIGHT = 479
    • main方法里内容删除
  • 画窗口

    • World类 继承 JPanel
    • main方法里内容复制
      public static void main(String[] args) {
              JFrame frame = new JFrame(); //3.
              World world = new World(); //会创建窗口中的那一堆对象
              world.setFocusable(true);
              frame.add(world);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.setSize(WIDTH+16, HEIGHT+40);
              frame.setLocationRelativeTo(null);
              frame.setResizable(false);
              frame.setVisible(true); //自动调用paint()方法
          }
      
  • 画海洋图、至少准备6个对象并画出来

    • SeaObject

      • 修改 SeaObject 为抽象类
      • 修改 move 为抽象方法
      • 创建 getImage 抽象方法
        • 返回值类型为 ImageIcon 类型
      • 定义两个状态常量(LIVE,DEAD)和一个变量 表示当前状态(state)
        • LIVE = 0 表示活着
        • DEAD = 1 表示死了
        • state = LIVE 表示默认为活着
      • 创建 isLive 方法
        • 返回值类型:boolean 类型
        • 业务:判断当前状态是否为活着
      • 创建 isDead 方法
        • 返回值类型:boolean 类型
        • 业务:判断当前状态是否为死了
      • 创建 paintImage 方法
        • 返回值类型 void

        • 参数 Graphics g

        • 业务:如果当前状态为活着,画图片

          /** 画对象 g:画笔 */
          public void paintImage(Graphics g){
              if(this.isLive()){ //若活着的
                  this.getImage().paintIcon(null,g,this.x,this.y); //----不要求掌握
              }
          }
          
    • Battleship

      • 重写 getImage 方法,返回对应的 ImageIcon 对象
    • ObserveSubmarine

      • 重写 getImage 方法,返回对应的 ImageIcon 对象
    • TorpedoSubmarine

      • 重写 getImage 方法,返回对应的 ImageIcon 对象
    • MineSubmarine

      • 重写 getImage 方法,返回对应的 ImageIcon 对象
    • Bomb

      • 重写 getImage 方法,返回对应的 ImageIcon 对象
    • Mine

      • 重写 getImage 方法,返回对应的 ImageIcon 对象
    • World

      • 创建战舰(Battleship对象,SeaObject数组对象,Mine数组对象,Bomb数组对象),并赋值

      • 重画

        /** 重写paint()画 g:系统自带的画笔 */
            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<mines.length;i++){ //遍历所有水雷
                    mines[i].paintImage(g); //画水雷
                }
                for(int i=0;i<bombs.length;i++){ //遍历所有炸弹
                    bombs[i].paintImage(g); //画炸弹
                }
            }
        
  • 测试

    • 修改 SeaObject 类,2个参数的构造方法,将负号去掉,为了能在屏幕上显示已经画好的对象
      在这里插入图片描述

    • 运行测试(海洋,战舰,潜艇[侦查潜艇,鱼雷潜艇,水雷潜艇],水雷,炸弹等对象是否显示在窗口里)

六、day06

今日目标:

深海杀手_day06效果视频

  • 清空成员变量数组中的元素
    • 潜艇数组 submarines
    • 水雷数组 mines
    • 炸弹数组 bombs
  • 定时做某件事
    • 定义 action 方法,作为游戏的启动执行方法
      • 由于游戏是要求定时做某件事,所以创建定时器对象 Timer,util 包下的 Timer
      • 调用 Timer对象中 schedule 方法 – 3个参数的方法
      • 在方法中将定时器中调用需要定时执行的方法,定时间隔,每10毫秒走一次
        • 潜艇入场
        • 水雷入场
        • 海洋对象移动
        • 重画
    • 在 main 方法中调用 action 方法
  • 潜艇入场
    • 生成潜艇对象方法(侦察潜艇、鱼雷潜艇、水雷潜艇)
      • 方法名 nextSubmarine
      • 返回值类型 SeaObject
        • 要求随机返回某一潜艇对象,为了统一返回的类型,所以使用父类 SeaObject 作为返回值类型
      • 方法具体实现
        • 业务:按照一定比例随机生成潜艇对象
          • 侦查潜艇:50%
          • 鱼雷潜艇:30%
          • 水雷潜艇:20%
        • 思路
          • 通过随机数的形式,加上if…else,返回对应的潜艇对象
    • 潜艇入场方法
      • 方法名 submarineEnterAction
      • 返回值类型 void
      • 方法具体实现
        • 业务:每400毫秒调用nextSubmarine方法生成一个潜艇对象
          • 每10豪秒走一次方法submarineEnterAction,现在想每400毫秒走一次/调用一个 nextSubmarine 生成潜艇对象
          • 之前我们已经清空了潜艇数组(为的就是想让潜艇对象从无到有)
          • 将生成的潜艇对象添加到World类中,成员变量(submarines)的潜艇数组中
        • 思路:添加一个计数的变量subEnterIndex,通过整除的方式(计数变量自增,取余),if判断等方式达到目的
  • 水雷入场(上)
    • 水雷入场方法
      • 方法名:mineEnterAction
      • 返回值类型:void
      • 方法具体实现
        • 业务:
          • 每1000毫秒方法生成一个水雷对象
          • 每10豪秒走一次方法mineEnterAction,现在想每1000毫秒走一次/调用一个 xxx方法(当日未实现) 生成潜艇对象
        • 思路:添加一个计数的变量mineEnterIndex,通过整除的方式(计数变量自增,取余),if判断等方式达到目的
  • 海洋对象移动
    • 方法名:moveAction
    • 返回值类型:void
    • 方法具体实现:
      • 业务:
        • 每10豪秒走一次方法moveAction
        • 分别循环遍历海洋对象(潜艇对象,水雷对象,炸弹)
        • 循环中调用每个子类中的重写的move方法

七、day07

  • 深水炸弹入场:

    • 业务:
      • 深水炸弹是由战舰发出的
      • 我们深水炸弹是通过键盘进行触发的
      • 将深水炸弹存放进炸弹数组
    • 思路:
      • 通过监听键盘,去调用创建深水炸弹的方法
      • 将深水炸弹存放进炸弹数组
      • 由于深水炸弹是由战舰发出的,坐标点为战舰的坐标点,所以这个生成深水炸弹对象的方法需要写在战舰类上(Battleship)
        // 可以参考,不需要必须会写
        KeyAdapter k = new KeyAdapter(){
            public void keyPressed(KeyEvent e) {
                if(e.getKeyCode() == KeyEvent.VK_SPACE){
                	// 深水炸弹入场
                }
            }
        };
        this.addKeyListener(k);
        
  • 战舰移动:

    • 业务:
      • 通过键盘进行触发战舰的左右移动
    • 思路
      • 和上述空格键触发深水炸弹一样,写在键盘出发的方法里
      • 通过不同的按键触发,调用不同的方法
        • 通过按 ← 箭头,触发 Battleship 中的 moveLeft 战舰左移方法

        • 通过按 → 箭头,触发 Battleship 中的 moveRight 战舰右移方法

          if(e.getKeyCode()==KeyEvent.VK_LEFT){ //不要求掌握--若抬起的是左箭头
            // 战舰左移
          }
          if(e.getKeyCode()==KeyEvent.VK_RIGHT){ //不要求掌握--若抬起的是右箭头
            //战舰右移
          }
          
      • moveLeft 和 moveRight 实现,移动 Battleship 的 x 坐标
  • 删除越界的海洋对象:

    • 方法名:outOfBoundsAction
    • 返回值类型:void
    • 业务:
      • 由于超越边界的潜艇,深水炸弹,鱼雷就没有作用了,并且占用内存资源,所以我们需要删除它们
        • 潜艇是超出右边的边界删除
        • 深水炸弹是超出底下的边界删除
        • 鱼雷是超过海平面伤处
    • 思路
      • 分别循环所有的海洋对象,判断是否越界,如果越界,从数组中删除
        • 判断是否越界
          • SeaObject(潜艇越界)
            • 定义 isOutOfBounds 方法,返回值类型为boolean类型
              • 潜艇的x坐标 >= 世界的WIDTH
          • Bomb(深水炸弹越界)
            • 重写 isOutOfBounds 方法
            • 深水炸弹的y坐标 >= 世界的WEIGHT
          • Mine(鱼雷越界)
            • 重写 isOutOfBounds 方法
            • 鱼雷的y坐标 <= 150(海平面的y坐标)- 鱼雷的高height
        • 数组删除
          • 将数组的最后一个元素赋值给想要删除的元素
          • 数组缩容
  • 设计EnemyScore得分接口、EnemyLife得命接口,侦察潜艇与鱼雷潜艇实现EnemyScore接口,水雷潜艇实现EnemyLife接口

    • EnemyScore 得分接口
      • 声明得分的抽象方法
        • 方法名:getScore
        • 返回值类型:int
      • 侦察潜艇和鱼类潜艇实现得分接口,重写 getScore 方法
        • 侦察潜艇得10分
        • 鱼类潜艇得40分
    • EnemyLife 得命接口
      • 声明得名的抽象方法
        • 方法名:getLife
        • 返回值类型:int
      • 水雷潜艇实现得命接口,重写 getLife 方法
        • 水雷潜艇得1条命

八、day08

  • 水雷入场(下)

    • 水雷入场之前我们已经将方法创建完成,mineEnterAction,但是具体的生成水雷对象及将水雷对象添加到水雷数组中未做,我们需要的就是根据之前的水雷入场继续做即可
    • 业务:
      • 生成水雷对象,并将水雷对象添加到水雷数组中
      • 由于是定时出现水雷,之后我们需要将 mineEnterAction 方法在定时器中调用
    • 思路:
      • 由于水雷对象是由水雷潜艇产生,水雷对象的初始坐标点与水雷潜艇的坐标点有关,所以我们应该在水雷潜艇类中创建生成水雷对象的方法 shootMine
      • 我们需要先遍历潜艇数组中所有的潜艇对象,再判断这些潜艇对象是否是水雷潜艇(instanceof),如果是水雷潜艇(MineSubmarine),调用创建水雷对象方法(shootMine),并且对水雷数组(mines)进行扩容,之后将水雷对象(Mine)添加到水雷数组中
  • 炸弹与潜艇的碰撞

    • 在 SeaObject 中设计 isHit() 检测碰撞方法

      • 由于碰撞的两者都是 SeaObject 类型,所以将方法写在 SeaObject 类中,
      • 由于碰撞是两个对象的事,所以我们在设计这个方法的时候,需要提供一个参数 (SeaObject other)
      • 由于这个方法是判断是否碰撞,所以返回值类型为 boolean 类型
      • 碰撞逻辑如下图所示:
        请添加图片描述
    • 参考代码:

      /**
       * 检测碰撞
       * @param other 另一个对象  this表示一个对象
       * @return 若撞上了则返回true,否则返回false
       */
      public boolean isHit(SeaObject other){
          //假设:this为潜艇,other为炸弹
          int x1 = this.x-other.width;  //x1:潜艇的x-炸弹的宽
          int x2 = this.x+this.width;   //x2:潜艇的x+潜艇的宽
          int y1 = this.y-other.height; //y1:潜艇的y-炸弹的高
          int y2 = this.y+this.height;  //y2:潜艇的y+潜艇的高
          int x = other.x; //x:炸弹的x
          int y = other.y; //y:炸弹的y     //练习-----------2:34继续
      
          return x>=x1 && x<=x2
                 &&
                 y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
      }
      
    • 在 SeaObject 中设计 goDead() 去死方法 – 变更状态

      • 业务:
        • 变更状态,将海洋对象的状态 state 从 LIVE 变为 DEAD
      • 方法名: goDead
      • 返回值: void
    • 在 Battleship 中设计 addLife() 增命方法

      • 业务:设计增命方法
      • 思路:给 Battleship 的 life 属性累加
      • 方法名:addLife
      • 参数:int num
    • 在 World 中设计 bombBangAction() 炸弹与潜艇的碰撞方法

      • 业务:
        • 判断炸弹与潜艇是否碰撞,如果碰撞,让炸弹和潜艇一起去死
        • 如果摧毁的是水雷潜艇得命
        • 如果摧毁的是侦察潜艇得10分
        • 如果摧毁的是鱼雷潜艇得40分
      • 思路:
        • 判断所有炸弹与潜艇是否碰撞
          • 首先需要嵌套循环两个数组(bombs 炸弹,submarines 潜艇)
          • 判断炸弹是否活着(isLive())
          • 判断潜艇是否活着(isLive())
          • 判断炸弹与潜艇是否碰撞(isHit())
        • 如果都满足则代表碰撞上了
          • 让炸弹去死(goDead),让潜艇去死(goDead)
          • 判断摧毁的是得分的潜艇(侦察/鱼雷)
            • 向下转型(引用类型强转)
            • 判断这个对象是否实现了得分接口 (instanceof)
            • 调用 getScore 方法
          • 判断摧毁的是得命的潜艇
            • 向下转型(引用类型强转)
            • 判断这个对象是否实现了得命接口 (instanceof)
            • 调用 getLife 获取命数的方法 和 addLife 增加命数的方法
        • 在定时器中调用 bombBangAction() 方法
  • 海洋对象死亡删除

    • 在 outOfBoundsAction 删除越界的海洋对象的方法中添加是否死了的判断
    • 将方法改为 越界和死亡 都删除删除对象
    • 参考代码:
      在这里插入图片描述
  • 画分和画命:

    • 在 Battleship 中设计 getLife() 获取命数
      • 方法名:getLife
      • 返回值类型:int – 获取 Battleship 中的 life 属性
    • 在World类的 paint() 中:画分和画命————不要求掌握
      g.drawString("SCORE: "+score,200,50);         //画分----不要求掌握
      g.drawString("LIFE: "+ship.getLife(),400,50); //画命----不要求掌握
      

九、day09

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/107634.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!