栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java面向对象学习练习———潜艇游戏(飞机游戏)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java面向对象学习练习———潜艇游戏(飞机游戏)

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

潜艇游戏第一天:
  1. 根据参与的角色创建6个类,创建World类并测试;                                                                    

潜艇游戏第二天:
  1. 给6个类添加构造方法,并测试;(以战舰类为例)                                                                 
    public class Battleship {
        int width;  //宽
        int height; //高
        int x;      //x坐标
        int y;      //y坐标
        int speed;  //移动速度
        int life;   //命数
        
        Battleship(){
            width = 66;
            height = 26;
            x = 270;
            y = 124;
            speed = 20;
            life = 5;
        }
        void move(){
            System.out.println("战舰移动啦!");
        }
    }

潜艇游戏第三天:
  1. 在World类中创建侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组,水雷数组,炸弹数组,并测试;                                                                                                                                           
    public class World {
        public static void main(String[] args) {
            ObserveSubmarine[] oses = new ObserveSubmarine[3];
            oses[0] = new ObserveSubmarine();
            oses[1] = new ObserveSubmarine();
            oses[2] = new ObserveSubmarine();
            for(int i=0;i 
  2. 设计SeaObject超类,6个类继承超类;(以战舰类为例)
    public class Battleship extends SeaObject {
        int life;   //命数
        
        Battleship(){
            super(66,26,270,124,20);
            life = 5;
        }
    }
  3. 在SeaObject中设计两个构造方法,6个派生类分别调用;    
    import java.util.Random;
    
    public class SeaObject {
        int width;  //宽
        int height; //高
        int x;      //x坐标
        int y;      //y坐标
        int speed;  //移动速度
        
        //因为三种潜艇的width/height的值都是不一样的,所以数据不能写死,需传参写活
        //因为三种潜艇的x/y/speed的值都是一样的,所以数据可以写死,不需要传参
        SeaObject(int width,int height){
            this.width = width;
            this.height = height;
            x = -width; //负的潜艇的宽
            Random rand = new Random(); //随机数对象
            y = rand.nextInt(479-height-150+1)+150; //150到(窗口高-潜艇高)之间的随机数
            speed = rand.nextInt(3)+1; //1到3之内的随机数
        }
        
        //因为三种对象的width/height/x/y/speed都是不一样的,所以数据不能写死,需传参写活
        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;
        }
        void move(){
            System.out.println("海洋对象移动啦!");
        }
    }

潜艇游戏第四天:
  1. 将侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组统一组合为SeaObject超类数组,在World类中改写并测试;
    public class World extends JPanel { //2.
        public static void main(String[] args) {
            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);
    
            Mine o2 = new Mine(100,200);
            System.out.println("水雷初始数据:   x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
            o2.move();
            System.out.println("水雷移动后数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
    
            SeaObject[] submarines = new SeaObject[5]; //潜艇数组
            submarines[0] = new ObserveSubmarine(); //向上造型
            submarines[1] = new ObserveSubmarine();
            submarines[2] = new TorpedoSubmarine();
            submarines[3] = new TorpedoSubmarine();
            submarines[4] = new MineSubmarine();
            for(int i=0;i 
  2. 在6个类中重写move()移动,并测试;(以战舰类为例)
    public class Battleship extends SeaObject {
        int life;   //命数
        
        Battleship(){
            super(66,26,270,124,20);
            life = 5;
        }
    
        
        void move(){
            //暂时搁置
        }
    }
  3. 在World类中画窗口:  
    import javax.swing.JFrame;
    import javax.swing.JPanel; //1.第一步
    
    public class World extends JPanel { //2.第二步
        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(641+16, 479+39);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    }
    

潜艇游戏第五天:
  1. 给类中成员添加访问控制修饰符;(以战舰类为例)                                                                访问控制修饰符可看:(13条消息) Java学习日记Day11(面向对象day05)_终为尘埃的博客-CSDN博客
    public class Battleship extends SeaObject {
        private int life;   //命数
        
        public Battleship(){
            super(66,26,270,124,20);
            life = 5;
        }
    
        
        public void move(){
            //暂时搁置
        }
    }
  2. 设计Images图片类;
    import javax.swing.ImageIcon;
    
    
    public class Images {
        public static ImageIcon sea;         //海洋图片
        public static ImageIcon gameover;    //游戏结束图片
        public static ImageIcon battleship;  //战舰图片
        public static ImageIcon obsersubm;   //侦察潜艇图片
        public static ImageIcon torpesubm;   //鱼雷潜艇图片
        public static ImageIcon minesubm;    //水雷潜艇图片
        public static ImageIcon mine;        //水雷图片
        public static ImageIcon bomb;        //炸弹图片
    
        static{ //初始化静态图片
            //将img中的sea.png读取到静态变量sea中
            sea = new ImageIcon("img/sea.png");
            gameover = new ImageIcon("img/gameover.png");
            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");
        }
    
        //测试图片是否读取成功
        public static void main(String[] args) {
            //返回8表示图片读取成功,返回其余数字表示图片读取失败
            System.out.println(sea.getImageLoadStatus()); //8
            System.out.println(gameover.getImageLoadStatus());
            System.out.println(battleship.getImageLoadStatus());
            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());
        }
    
    }
    

潜艇游戏第六天:
  1. 在World类中设计窗口的宽和高为常量,适当地方做修改,重写getImage()方法(以战舰类为例);

    import javax.swing.ImageIcon;
    
    public class Battleship extends SeaObject {
        private int life;   //命数
        
        public Battleship(){
            super(66,26,270,124,20);
            life = 5;
        }
    
        
        public void move(){
            //暂时搁置
        }
    
        
        public ImageIcon getImage(){
            return Images.battleship; //返回战舰图片
        }
    }
    
  2. 画对象:

    1)想画对象需要获取对象的图片,每个对象都得获取图片,
     意味着获取图片行为为共有行为,所以设计在SeaObject超类中,
     每个对象获取图片的代码都是不一样的,所以设计为抽象方法
     ----在SeaObject中设计为抽象方法getImage()获取对象的图片
    2)在派生类中重写getImage()获取对象图片
     ----在6个类中重写getImage()返回不同的图片
    3)因为只有活着的对象才需要画到窗口中,所以需要设计对象的状态(活着还是死了),
     每个对象都有状态,意味着状态为共有属性,所以设计在SeaObject超类中,
     状态一般都设计为常量,同时再设计state变量表示当前状态
     ----在SeaObject中设计LIVE、DEAD常量,state变量表示当前状态
     在后期的业务中经常需要判断对象的状态,每个对象都得判断,
     意味着判断状态的行为为共有行为,所以设计在SeaObject超类中,
     每个对象判断状态的代码都是一样的,所以设计为普通方法
     ----在SeaObject中设计isLive()、isDead()判断对象的状态
    4)数据(状态、图片、x坐标、y坐标)都有了就可以开画了,每个对象都得画,
     意味着画对象行为为共有行为,所以设计在SeaObject超类中,
     每个对象画的代码都是一样的,所以设计为普通方法
     ----在SeaObject中设计paintImage()画对象
    5)画对象的行为做好了,在窗口World类中调用即可
     5.1)准备对象
     5.2)重写paint()方法-----在paint()中调用paintImage()画对象即可
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.util.Arrays;


public class World extends JPanel {
    public static final int WIDTH = 641;  //窗口的宽
    public static final int HEIGHT = 479; //窗口的高

    //如下这一堆对象就是窗口中你所看到的对象了
    private Battleship ship = new Battleship(); //战舰对象
    private SeaObject[] submarines = {
            new ObserveSubmarine(),
            new TorpedoSubmarine(),
            new MineSubmarine()
    }; //潜艇数组
    private Mine[] mines = {
            new Mine(280,300)
    }; //水雷数组
    private Bomb[] bombs = {
            new Bomb(200,250)
    }; //炸弹数组

    
    public void paint(Graphics g){
        Images.sea.paintIcon(null,g,0,0); //画海洋图
        ship.paintImage(g); //画战舰
        for(int i=0;i 

潜艇游戏第七天:
  1. 潜艇入场:

    • 潜艇是由窗口产生的,所以在窗口World类中设计nextSubmarine()生成潜艇对象;

      private SeaObject nextSubmarine(){
          Random rand = new Random(); //随机数对象
          int type = rand.nextInt(20); //0到19之间的随机数
          if(type<10){ //0到9时,返回侦察潜艇对象
              return new ObserveSubmarine();
          }else if(type<16){ //10到15时,返回鱼雷潜艇对象
              return new TorpedoSubmarine();
          }else{ //16到19时,返回水雷潜艇对象
              return new MineSubmarine();
          }
      }
    • 潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场

      在submarineEnterAction()中:

      ​ 每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到最后一个元素上 ;

      private int subEnterIndex = 0; //潜艇入场计数
      
      private void submarineEnterAction(){ //每10毫秒走一次
          subEnterIndex++; //每10毫秒增1
          if(subEnterIndex%40==0){ //每400(40*10)毫秒走一次
              SeaObject obj = nextSubmarine(); //获取潜艇对象
              submarines = Arrays.copyOf(submarines,submarines.length+1); //扩容
              submarines[submarines.length-1] = obj; //将obj添加到submarines的最后一个元素上
          }
      }
      private void action(){
          Timer timer = new Timer(); //定时器对象
          int interval = 10; //定时间隔(以毫秒为单位)
          timer.schedule(new TimerTask() {
              public void run() { //定时干的事---每10毫秒自动调用
                  submarineEnterAction(); //潜艇(侦察潜艇、鱼雷潜艇、水雷入场)入场
                  repaint(); //重画---系统自动调用paint()方法
              }
          }, interval, interval); //定时日程表
      }

      注意:在run()中调用submarineEnterAction()之后,一定要调用repaint()来重画

  2. 水雷入场:

    • 水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象

    • 水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场

      在mineEnterAction()中:

      ​ 每1000毫秒,

      private int mineEnterIndex = 0; //水雷入场计数
      
      private void mineEnterAction(){ //每10毫秒走一次
          mineEnterIndex++; //每10毫秒增1
          if(mineEnterIndex%100==0) { //每1000毫秒走一次
              //暂时搁置
          }
      }
  3. 海洋对象移动:

    • 海洋对象移动为共有行为,所以在SeaObject中设计抽象方法move()实现移动,派生类中重写

    • 海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动

      在moveAction()中:

      ​ 遍历所有潜艇--潜艇动,遍历所有水雷--水雷动,遍历所有炸弹--炸弹动 

      private void moveAction(){ //每10毫秒走一次
          for(int i=0;i 

潜艇游戏第八天:
  1. 炸弹入场:

    • 炸弹是由战舰发射出来的,所以在Battleship类中设计shootBomb()生成炸弹对象;

      public Bomb shootBomb(){
          return new Bomb(this.x,this.y); //炸弹的初始坐标就是战舰的坐标
      }
    • 炸弹入场为按空格键事件触发的,所以在侦听器中重写keyReleased()按键抬起事件,在抬起事件中:

      • 判断若抬起的是空格键,则:

        ​ 获取炸弹对象obj,bombs扩容,将obj添加到bombs的最后一个元素上

  2. 战舰移动:

    • 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
    • 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起事件中:
      • 判断若抬起的是左箭头,则战舰左移
      • 判断若抬起的是右箭头,则战舰右移 
        //键盘侦听器
        KeyAdapter k = new KeyAdapter() {
            
            public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发
                if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键
                    Bomb obj = ship.shootBomb(); //获取炸弹对象
                    bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
                    bombs[bombs.length-1] = obj; //将obj添加到最后一个元素上
                }
                if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左箭头
                    ship.moveLeft(); //战舰左移
                }
                if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //若抬起的是右箭头
                    ship.moveRight(); //战舰右移
                }
            }
        };
        this.addKeyListener(k); //添加侦听
  3. 删除越界的海洋对象:

    • 在SeaObject中设计isOutOfBounds()检测潜艇是否越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷是否越界;

    • 删除越界海洋对象为定时发生的,所以在run()中调用outOfBoundsAction()删除越界海洋对象

      在outOfBoundsAction()中:

      ​ 遍历所有潜艇/水雷/炸弹,判断若越界了:

      ​ 将越界元素替换为数组的最后一个元素,缩容;

      private void outOfBoundsAction(){ //每10毫秒走一次
          for(int i=0;i 
  4. 设计EnemyScore得分接口,ObserveSubmarine和TorpedoSubmarine实现得分接口;

    public interface EnemyScore {
        
        public int getScore();
    }

    设计EnemyLife得命接口,MineSubmarine实现得命接口;

    public interface EnemyLife {
        
        public int getLife();
    }

潜艇游戏第九天:
  1. 水雷入场:--------------后半段

    • 水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;

      public Mine shootMine(){
          //水雷的x:水雷潜艇的x+水雷潜艇的width
          //水雷的y:水雷潜艇的y-水雷的高(11)
          return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象
      }
    • 水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场

      在mineEnterAction()中:

      ​ 每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,

      ​ 获取水雷对象obj,mines扩容,将obj装到最后一个元素上;

      private int mineEnterIndex = 0; //水雷入场计数
      
      private void mineEnterAction(){ //每10毫秒走一次
          mineEnterIndex++; //每10毫秒增1
          if(mineEnterIndex%100==0) { //每1000毫秒走一次
              for(int i=0;i 
  2. 炸弹与潜艇的碰撞:

    • 在SeaObject中设计isHit()检测碰撞、goDead()去死; 

      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
          return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
      }
      
      
      public void goDead(){
          state = DEAD; //将当前状态修改为DEAD死了的
      }
    • 在Battleship中设计addLife()增命;

      public void addLife(int num){
          life += num; //命数增num
      }
    • 炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞

      在bombBangAction()中:

      ​ 遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:

      ​ 炸弹去死、潜艇去死;

      ​ 判断若是分,则强转为得分接口,玩家得分;

      ​ 判断若是命,则强转为得命接口,获取命数,战舰得命;

      private int score = 0 ; //玩家得分
      
      private void bombBangAction(){ //每10毫秒走一次
          for(int i=0;i 
  3. 画分和画命:

    • 在Battleship中设计getLife()获取命数;                                                                                  
      public int getLife(){
          return life; //返回命数
      }
    • 在World的paint()中:画分和画命;                                                                                     
      public void paint(Graphics g){ //每10毫秒走一次
          Images.sea.paintIcon(null,g,0,0); //画海洋图
          ship.paintImage(g); //画战舰
          for(int i=0;i 

潜艇游戏第十天:
  1. 水雷与战舰的碰撞:

    • 在Battleship中设计subtractLife()减命;

      public void subtractLife(){
          life--; //命数减1
      }
    • 水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;

      在mineBangAction()中:

      ​ 遍历所有水雷,得水雷,判断若都活着并且还撞上了:

      ​ 水雷去死、战舰减命; 

      private void mineBangAction(){ //每10毫秒走一次
          for(int i=0;i 
  2. 检测游戏结束:

    • 借用Battleship的getLife()获取命数;

    • 检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束

      在checkGameOverAction()中:

      ​ 判断若战舰的命数<=0,表示游戏结束了;

      private void checkGameOverAction(){ //每10毫秒走一次
          if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了
              state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态
          }
      }
  3. 画状态:

    • 在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;

    • 在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;

    • 在paint()中设计:若为游戏结束状态则画游戏结束图;

    • 设计run()中那一堆代码,为仅在运行状态下执行;

    • 设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;

    • 在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。       

       另外还实现一个飞机小游戏,过程相似。结果如图:

      如有需要,可评论区留下邮箱,后会发送。 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1024566.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号