小游戏项目2 - SkyFight 空战

发布时间 2023-07-18 15:43:30作者: 大宝贝94106

小游戏项目2 - SkyFight 空战

  1. 新建项目 新建image包 复制图片到包中

  2. 打包图片 : 创建Image(图片工具类)类 , 把图片封装成对象 , 以便接下来调用.

    //Image类
    //先把图片的地址封装为一个具体地址对象,
    static URL shellURL = Image.class.getResource("/image/shell.png");
    //把图片封装成一个对象,必须通过它的地址来封装
    static ImageIcon shell = new ImageIcon(shellURL);
    //变量用static修饰后,在使用时可以 类名.属性名 调用了,static用到的东西都得要static的,所以shellURL也要用static修饰
    
  3. 创建窗体 : 创建startGame(开始游戏类)类, 在类中main方法中创建窗体并初始化.

    //startGame类
    //创建一个窗体
    JFrame jf = new JFrame();
    //给窗体加一个标题
    jf.setTitle("SkyFight");
    //获得屏幕的宽高
    int width = Toolkit.getDefaultToolkit().getScreenSize.width; 
    int height = Toolkit.getDefaultToolkit().getScreenSize.height;
    //设置窗体的弹出位置和大小,参数-位置的X,Y 窗体的宽和高
    jf.setBounds((width-x)/2,(height-y)/2,x,y);//窗体出现在屏幕中间
    //设置窗体大小不可变
    jf.setResizable(false); 
    //关闭窗体的同时,关闭程序
    jf.setDefaultCloseOperation(EXIT_ON_CLOSE);
    //让窗体可见
    jf.setVisible(true);
    
  4. 绘制图案 : 创建 gamePanel(游戏面板) 类, 继承 JPanel类 , 它是一个透明的面板, 我们所有的内容都在这个面板上, 然后把面板添加到窗体中 , 在面板中绘制背景,飞机和子弹(静态). 在面板类构造器里初始化飞机和子弹的位置 (调用初始化游戏的方法). 用数组定义炮弹坐标.

    //游戏面板类 继承JPanel
    public class gamePanel extends JPanel {
        //定义飞机坐标
        int planeX,planeY;
        //子弹的X轴坐标
        int []shellX = new int[10];
        //子弹的Y轴坐标
        int []shellY = new int[10];
        //--初始化游戏方法--
        public void init(){
            //初始化飞机坐标
            planeX = 300;
            planeY = 400;
            //初始化子弹的坐标,10个子弹在一处 100,100
            for(int i = 0;i < 10 ; i++){
                shellX[i] = 100;
                shellY[i] = 100;
            }
        }
       //--构造器--
        public gamePanel(){
            init();
        }
        //重写paintComponent方法,用来在面板中绘制图案
        @Override
        protected void paintComponent(Graphics g) {//参数:相当于画笔
            super.paintComponent(g);
            //给面板设置一个背景颜色
            this.setBackground(new Color(r,g,b));
            //给面板设置背景图片
            Image.sky.paintIcon(this,g,0,0);//参数:当前面板,画笔,坐标xy
            Image.plane.paintIcon(this,g,planeX,planeY);//绘制出飞机
            //绘制10个子弹
            for(int i = 0; i < 10; i++){
                Image.shell.paintComponent(this,g,shellX[i],shellY[i]);
            }
    }
    }
    //startGame类
    	gamePanel gp = new gamePanel();//创建面板对象
    	jf.add(gp);//把面板gp添加到窗体jf中
    
    
  5. 空格暂停效果 : 加入键盘监听, 在gamePanel类的构造器中加入KeyListener方法, 参数为(new KeyAdapter对象) , 用e.getKeyCode()方法获得键位值, 别忘了要把焦点放在面板上.

    //gamePanel类-----
    //创建属性isStart游戏是否开始,默认false
    boolean isStart = false;
    //构造器中加入键盘监听
    this.setFocusable(true);//把焦点放在面板上
    this.addKeyListener(new KeyAdapter());
     @Override
                public void keyPressed(KeyEvent e) {    //键盘按下监听
                    super.keyPressed(e);
                    int keyCode = e.getKeyCode();  //获取键位值
                    System.out.println(keyCode);
                    if(keyCode == 32){//监听到按空格
                        //点击空格后,游戏的状态相反
                        isStart = !isStart;
                        //调用repaint方法,重新执行paintComponent方法,相当于刷新游戏
                        repaint();
                    }
    //在paintComponent方法中------                
    //当游戏暂停时,面板中打印一行文字
    	if(isStart==false){
            g.setColor(new Color(r,g,b));
            g.setFont(new Font( "黑体", Font.BOLD, 40));
            g.drawString("点击空格开始游戏", 0 ,0);//绘制文字
        }                
                 
    
  6. 让飞机动起来: 定义上下左右变量和定时器, 监听键盘的上下左右给变量赋值, 在再监听器的KeyReleased方法中把4个变量的值赋值回false,实现松开按键飞机停下 . 实现飞机移动要靠定时器, 创建定时器, 要在程序启动时初始化, 所以它要在构造器中, 参数如下. 在方向变量true时改变XY的值实现对象移动 , 加入isStart是true的条件判断, 调用repaint方法刷新画面, 最后启动定时器.

    //在类中定义4个布尔类型变量 上下左右 ,来控制是否激活飞机向各个方向移动.
    boolean up,down,left,rignt;
    //在监听器中添加对上下左右的监听
    if(keyCode==KeyEvent.VK_UP){//KeyEvent.VK_UP是封装好的获得键位方法
        up = true;
    }
    if(keyCode==KeyEnent.VK_DOWN){
    	down = true;
    }...
    //抬起按键后再赋值为false
            @Override
            public void keyReleased(KeyEvent e) {
                super.keyReleased(e);
                int keyCode = e.getKeyCode();//获取键位值
                if (keyCode == KeyEvent.VK_UP){
                    up = false;
                }
                if (keyCode == KeyEvent.VK_DOWN){
                    down = false;
                }...    
    //先在类中定义一个定时器
        Timer timer;
    //程序启动时初始化,在构造器中加入定时器 
    //初始化定时器: 每50毫秒执行一下ActionPerformed中的逻辑    
    	timer = new Timer(50,new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e){
    		if(isStart==true){ //游戏运行时才能移动         
                if(up==true){
                planeX -= 7;
            }
             if(down==true){
                 planeX += 7;
             }...
                 repaint();//刷新画面
            }
            }
        });   
        //定时器一定要启动
        timer.start();
    
  7. 让炮弹动起来, 创建炮弹角度的数组, 在初始化方法中,初始化炮弹的位置和角度, 并随机给角度赋值. 在定时器方法中通过循环让炮弹按随机数组中的角度移动 , 当炮弹移动出面板时,在反方向出现: 当X值到达最右边610时,X赋值为0,以此类推

    //类中创建炮弹角度数组
    int [] degrees = new [10];
    //在init()方法中,初始化炮弹的位置和角度(随机)
    for(int i = 0; i< 10 ; i++){ //10个炮弹
        shellX[i] = 100;
        shellY[i] = 100; //炮弹初始位置
        /*炮弹的移动角度为0~360之间,也就是0~2PI之间.
        random的取值范围:
        Math.random() --> [0.0 ~ 1.0)
        如果要[0~5]的整数
        Math.random()*6 --> [0.0 ~ 6.0)
        (int)(Math.random()*6) --> [0 ~ 5]
        如果要[0 ~ 2PI] 如下:
        */
        degrees[i] = (int)(Math.random()*2*Math.PI);
    }
    //在定时器的actionPerformed方法中添加循环
    //炮弹按着角度移动
    for(int i = 0 ; i < 10 ; i++){
        shellX[i] += Math.cos(degrees[i])*7;
        shellY[i] += Math.sin(degrees[i])*7;  
    //让弹移动出面板时,在反方向出现
    if(shellX[i] > 610){
        shell[i] = 0;
    }   
    if(shellX[i] < 0){
        shell[i] = 610;
    }...    
    }
    
  8. 碰撞检测: Rectangle方法把对象封装成矩形, intersects方法碰撞检测, 两个对象碰撞了就返回true. 如果是true,那么就在绘制方法中显示 '游戏结束' . 在类中创建布尔类型变量表示飞机生死的状态, 飞机死了后,要让画面停止 : 要在定时器方法中的 if 语句中加上一个条件, '游戏开始' && '飞机没死' . 有这两个条件游戏才能进行.

    //在类中创建布尔类型变量表示飞机生死的状态,默认false
    boolean isDie = false;
    
    /*在定时器方法中
      把飞机和炮弹封装为矩形,用intersects方法来检测碰撞,返回布尔类型变量
      封装矩形对象时要传入四个参数: 1.飞机的X轴坐标 2.飞机的Y轴坐标 3.矩形的款 4.高
      */
    boolean flag = new Rectangle(planeX, planeY, 64, 64).intersects(new Rectangle(shellX[i], shellY[i], 64,64));
    	if(flag){	//如果碰撞
            isDie = true;	//飞机死
        }
    
    //在paintComponent方法中添加绘制文字
    if(isDie){	//如果飞机死了
        g.drawString("飞机死了,重新开始",100,100);	//绘制文字
    }
    
    //到这里运行游戏,飞机死了后还能移动,所以要在定时器方法中的 if 语句中加上一个条件
    @Override
              public void actionPerformed(ActionEvent e) {
                  //键盘控制飞机 当游戏开始并飞机没死的时候 飞机和炮弹能动
                  if (isStart&&!isDie) {
                      if (up) {   //向上为Y轴减
                          planeY -= 10;
                      }
                      if (down) {
                          planeY += 10;
                      }
    
  9. 飞机死了后,按空格重新游戏 : 在键盘监听空格的方法中, 加一个条件判断, 如果飞机是活的, 那就暂停(和原来一样), 如果飞机是死的, 那就调用 初始化方法 重开游戏 , 在把飞机变活.

    if(keyCode==KeyEvent.VK_SPACE){
        if(isDie){	//如果飞机时死的
            init();	//初始化游戏
            isDie = false;	//再把状态改过来
        }else{
            isStart = !isStart;//飞机活着就是暂停
        }
    }
    
  10. 玩了几秒 : 定义开始和结束时间, 开始时间在构造方法开头, 结束时间在碰撞检测方法中. 两个变量的差就是你游玩的时间.

    //在类中创建开始和结束时间
    long startTime, endTime;
    
    //在构造器中给startTime赋值
    public GamePanel(){
        //获取游戏开始时间
        startTime = System.currentTimeMillis();
    }
    
    //在碰撞检测方法中给endTime赋值
    //碰撞检测, 
    boolean flag = new Rectangle(planeX,planeY,50,50).intersects(new Rectangle(shellX[i],shellY[i],50,50));
       //如果flag为true,
         if (flag){
           isDie = true;
           isStart = false;
           endTime = System.currentTimeMillis();//--结束游戏的时间
         }
    
    //显示游玩时间
    //在paintComponent方法中添加
    if(isDie){
        g.drawString("游戏结束,你游玩了" + (endTime - startTime)/1000 + "秒",100,100);
    }
    
  11. 解决飞机死了后重新开始游戏时间累加的问题 : 把给开始时间赋值的代码移动到初始化方法中.

  12. 给飞机添加死后爆炸效果 : 在paintComponent方法中, 换一个图片对象.

    //如果飞机死了,飞机plane 就变成 爆炸bang.
            if (isDie){
                Image.bang.paintIcon(this,g,planeX,planeY);//-----爆炸图片
            }else {
                Image.plane.paintIcon(this,g,planeX,planeY);//-----飞机图片
            }