我的第一个项目(十一) :飞机大战分包完成(简单阐述分包思路以及过程)

第一个,项目,十一,飞机,大战,分包,完成,简单,阐述,思路,以及,过程 · 浏览次数 : 292

小编点评

The code you provided is a JavaScript program that implements a game called "Simple Game". **Key Features:** * The game features a canvas element that displays the game world. * Players can move a hero object around the canvas. * There are enemies and a background in the game. * The game has different states, including start, running, paused, and end. * The game uses the canvas element to draw the game world, hero, enemies, and background. **How to Run the Game:** 1. Save the code in a file named `index.js`. 2. Create a new HTML file and give it the extension ".html". 3. Copy and paste the code from `index.js` into the HTML file. 4. Save the HTML file. 5. Open the HTML file in a web browser. **How to Play the Game:** 1. The game will automatically start when you open the HTML file in a web browser. 2. Use the arrow keys to move the hero. 3. The game will update the game state and draw the game elements accordingly. 4. The game will continue until the player presses the "P" key or reaches the end of the canvas. **Code Structure:** The code is organized into the following modules: * `index.js`: Main entry point for the game. * `config.js`: Contains constants and variables used throughout the game. * `Main`: Class that represents the main game object. * `hero`: Class that represents the player character. * `enemy`: Class that represents the enemies. * `background`: Class that represents the game background. **Additional Notes:** * The `config.js` file includes a variable called `copyright`, which contains an image of the game logo. * The `Main` class contains a method called `changebg()` that is called when the background image changes. * The `canvas` element in `index.js` is created dynamically when the HTML file is loaded.

正文

好家伙,

 

代码已开源

Git:

https://gitee.com/tang-and-han-dynasties/panghu-planebattle-esm.git

NPM:

panghu-planebattle-esm - npm (npmjs.com)

 

现在,比如说,我用Vue写好了个人博客主页的前端

我想在这个主页里面加点东西,让我的博客更缤纷多彩一点

我想在他的主页里面塞个小游戏,他会怎么做

 

1.思考步骤

如下:

第一步:去网上找个小游戏的资源,将这个包下载到本地,

诶,正好发现有个飞机大战 panghu-planebattle-modular 小游戏开发好了

我可以直接下载,或者通过npm安装

npm install panghu-planebattle-modular

 

第二步:导入到某个.vue文件或html文件

通过import导入

 

第三步:划一个区域<div>给这个包去渲染游戏

 剩下的他就不用管了

大概是这么个过程,然后我们按着这个思路,反向去分我们这个包

 

先来看看原先的完整代码:

完整代码

<template>
    <div>
    <h1>欢迎来到主页面</h1>
      <div ref="stage"></div>
    </div>
  </template>
  
  <script>
  export default {
    mounted() {
        //canvas初始化
        console.log("我被执行啦")
        let canvas = document.createElement('canvas');
        this.$refs.stage.appendChild(canvas);
        canvas.width = 480;
        canvas.height = 650;
        canvas.ref = canvas;
        canvas.style = "border: 1px solid red;"
        const context = canvas.getContext("2d");
  
        //图片初始化方法
        function createImage(src) {
          let img;
          if (typeof src === "string") {
            img = new Image();
            img.src = require('./img/' + src);
          } else {
            img = [];
            for (let i = 0; i < src.length; i++) {
              img[i] = new Image();
              img[i].src = require('./img/' + src[i]);
            }
          }
          return img;
        }
        //createImage()方法测试样例
        // let bg = createImage("4.jpg")
        // bg.onload = function () {
        //   console.log("img加载完毕")
        //   context.drawImage(bg, 0, 0, 480, 650)
        // }
        const IMAGES = {
          b: "bullet1.png",
          bg: "4.png",
          copyright: "shoot_copyright.png",
          pause: "game_pause.png",
          loading_frame: ["game_loading1.png", "game_loading2.png", "game_loading3.png",
            "game_loading4.png"
          ],
          hero_frame_live: ["hero1.png", "hero2.png"],
          hero_frame_death: ["hero_blowup_n1.png", "hero_blowup_n2.png", "hero_blowup_n3.png",
            "hero_blowup_n4.png"
          ],
          e1_live: ["enemy1.png"],
          e1_death: ["enemy1_down1.png", "enemy1_down2.png", "enemy1_down3.png", "enemy1_down4.png"],
          e2_live: ["enemy2.png"],
          e2_death: ["enemy2_down1.png", "enemy2_down2.png", "enemy2_down3.png", "enemy2_down4.png"],
          e3_live: ["enemy3_n1.png", "enemy3_n2.png"],
          e3_death: ["enemy3_down1.png", "enemy3_down2.png", "enemy3_down3.png", "enemy3_down4.png",
            "enemy3_down5.png", "enemy3_down6.png"
          ],
          c1: "lanqiu.png"
        };
        //初始化各个图片
        const b = createImage(IMAGES.b);
        const bg = createImage(IMAGES.bg);
        const copyright = createImage(IMAGES.copyright);
        const pause = createImage(IMAGES.pause);
        const loading_frame = createImage(IMAGES.loading_frame);
        const hero_frame = {
          live: createImage(IMAGES.hero_frame_live),
          death: createImage(IMAGES.hero_frame_death),
        };
        const e1 = {
          live: createImage(IMAGES.e1_live),
          death: createImage(IMAGES.e1_death),
        };
        const e2 = {
          live: createImage(IMAGES.e2_live),
          death: createImage(IMAGES.e2_death),
        };
        const e3 = {
          live: createImage(IMAGES.e3_live),
          death: createImage(IMAGES.e3_death),
        };
        const c1 = createImage(IMAGES.c1);
  
        //配置项:
        // 定义游戏的状态
        // 开始
        const START = 0;
        // 开始时
        const STARTING = 1;
        // 运行时
        const RUNNING = 2;
        // 暂停时
        const PAUSE = 3;
        // 结束时
        const END = 4;
        // 加载中
        const LOADINGING = 5;
  
        //state表示游戏的状态 取值必须是以上的五种状态
        let state = LOADINGING;
        // hero_frame.addEventListener("load", () => {
        //   state = START;
        // })
  
        pause.onload = function () {
          state = START;
          console.log(state)
        }
  
        //score 分数变量 life 变量
        let score = 0;
        let life = 3;
  
        //天空类的配置项
        const SKY = {
          bg: bg,
          width: 480,
          height: 650,
          speed: 10,
        };
  
        // 飞机加载界面的配置项
        const LOADING = {
          frame: loading_frame,
          width: 186,
          height: 38,
          x: 0,
          y: 650 - 38,
          speed: 400,
        };
  
        // 英雄配置项
        const HERO = {
          frame: hero_frame,
          width: 99,
          height: 124,
          speed: 100,
        };
  
        // 子弹配置项
        const BULLET = {
          img: b,
          width: 9,
          height: 21,
        };
  
        //小敌机配置项
        const E1 = {
          type: 1,
          width: 57,
          height: 51,
          life: 10,
          score: 1,
          frame: e1,
          minSpeed: 20,
          maxSpeed: 10
        };
        //中敌机配置项
        const E2 = {
          type: 2,
          width: 69,
          height: 95,
          life: 50,
          score: 5,
          frame: e2,
          minSpeed: 50,
          maxSpeed: 20
        };
        //打敌机配置项
        const E3 = {
          type: 3,
          width: 169,
          height: 258,
          life: 100,
          score: 20,
          frame: e3,
          minSpeed: 100,
          maxSpeed: 100
        };
        //奖励类配置项
        const C1 = {
          type: 4,
          width: 75,
          height: 75,
          life: 1,
          score: 1,
          img: c1,
          minSpeed: 5,
          maxSpeed: 10
        };
        //正式代码
  
        //初始化奖励类
        class Award {
          constructor(config) {
            this.type = config.type;
            this.width = config.width;
            this.height = config.height;
            this.x = Math.floor(Math.random() * (480 - config.width));
            this.y = -config.height;
            this.life = config.life;
            this.score = config.score;
            this.img = config.img;
            this.live = true;
            this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;
            this.lastTime = new Date().getTime();
            this.deathIndex = 0;
            this.destory = false;
          }
          move() {
            const currentTime = new Date().getTime();
            if (currentTime - this.lastTime >= this.speed) {
              if (this.live) {
                this.y = this.y + 6;
                this.lastTime = currentTime;
              } else {
                this.destory = true;
  
              }
            }
          }
          paint(context) {
            context.drawImage(this.img, this.x, this.y, this.width, this.height);
          }
          outOfBounds() {
            if (this.y > 650) {
              return true;
            }
          }
          hit(o) {
            let ol = o.x;
            let or = o.x + o.width;
            let ot = o.y;
            let ob = o.y + o.height;
            let el = this.x;
            let er = this.x + this.width;
            let et = this.y;
            let eb = this.y + this.height;
            if (ol > er || or < el || ot > eb || ob < et) {
              return false;
            } else {
              return true;
            }
          }
          // collide() {
          //   this.life--;
          //   if (this.life === 0) {
          //     this.live = false;
          //     score += this.score;
          //   }
          // }
        }
  
        //
        //初始化一个子弹类
        class Bullet {
          constructor(config, x, y) {
            this.img = config.img;
            this.width = config.width;
            this.height = config.height;
            this.x = x;
            this.y = y;
            this.destory = false;
          }
          //子弹绘制方法
          paint(context) {
            context.drawImage(this.img, this.x, this.y);
          }
          //移动子弹 this.y--
          move() {
            this.y -= 8;
          }
          outOfBounds() {
            //如果返回的是真的话 那么我们应该销毁掉这个子弹
            return this.y < -this.height;
          }
          collide() {
            //让这颗子弹变成可销毁状态
            this.destory = true;
          }
        }
        //
  
        // 初始化一个敌机类
        class Enemy {
  
  
  
  
  
          
          constructor(config) {
            this.type = config.type;
            this.width = config.width;
            this.height = config.height;
            this.x = Math.floor(Math.random() * (480 - config.width));
            this.y = -config.height;
            this.life = config.life;
            this.score = config.score;
            this.frame = config.frame;
            this.img = this.frame.live[0];
            this.live = true;
            this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;
            this.lastTime = new Date().getTime();
            this.deathIndex = 0;
            this.destory = false;
          }
          move() {
            const currentTime = new Date().getTime();
            if (currentTime - this.lastTime >= this.speed) {
              if (this.live) {
                this.img = this.frame.live[0];
                this.y++;
                this.lastTime = currentTime;
              } else {
                this.img = this.frame.death[this.deathIndex++];
                if (this.deathIndex === this.frame.death.length) {
                  this.destory = true;
                }
              }
            }
          }
          paint(context) {
            context.drawImage(this.img, this.x, this.y);
          }
          outOfBounds() {
            if (this.y > 650) {
              return true;
            }
          }
          hit(o) {
            let ol = o.x;
            let or = o.x + o.width;
            let ot = o.y;
            let ob = o.y + o.height;
            let el = this.x;
            let er = this.x + this.width;
            let et = this.y;
            let eb = this.y + this.height;
            if (ol > er || or < el || ot > eb || ob < et) {
              return false;
            } else {
              return true;
            }
          }
          collide() {
            this.life--;
            if (this.life === 0) {
              this.live = false;
              score += this.score;
            }
          }
        }
  
        //
        // 初始化一个英雄类
        class Hero {
          constructor(config) {
            this.width = config.width;
            this.height = config.height;
            this.x = (480 - config.width) / 2;
            this.y = 650 - config.height;
            this.frame = config.frame;
            this.frameLiveIndex = 0;
            this.frameDeathIndex = 0;
            this.lastTime = new Date().getTime();
            this.speed = config.speed;
            //当前展示的图片
            this.img = null;
            this.live = true;
            //子弹上次射击的时间
            this.lastShootTime = new Date().getTime();
            //子弹射击的间隔
            this.shootInterval = 50;
            //子弹夹数组
            this.bulletList = [];
            this.destory = false;
          }
          judge() {
            const currentTime = new Date().getTime();
            if (currentTime - this.lastTime > this.speed) {
              if (this.live) {
                this.img = this.frame.live[this.frameLiveIndex++ % this.frame.live.length];
              } else {
                //0 1 2 3 4
                this.img = this.frame.death[this.frameDeathIndex++];
                //到4的时候英雄死了
                if (this.frameDeathIndex === this.frame.death.length) {
                  this.destory = true;
                }
              }
              this.lastTime = currentTime;
            }
          }
          paint(context) {
            context.drawImage(this.img, this.x, this.y, this.width, this.height);
          }
          //英雄可以射击子弹
          shoot() {
            //获取当前时间
            const currentTime = new Date().getTime();
            //飞机的位置
            if (currentTime - this.lastShootTime > this.shootInterval) {
              //在飞机的头部初始化一个子弹对象
              let bullet = new Bullet(BULLET, this.x + this.width / 2 - BULLET.width / 2, this.y - BULLET.height);
              //英雄飞机要认领这个子弹
              this.bulletList.push(bullet);
              //在网页上绘制一个子弹对象
              bullet.paint(context);
              //更新英雄射击时间
              this.lastShootTime = currentTime;
            }
          }
          collide() {
            //将活着标识符切换为false
            //活着 -> 爆炸中 -> 死亡(销毁)
            this.live = false;
          }
        }
        //
        // 初始化一个飞机界面加载类
        class Loading {
          constructor(config) {
            this.frame = config.frame;
            this.frameIndex = 0;
            this.width = config.width;
            this.height = config.height;
            this.x = config.x;
            this.y = config.y;
            this.speed = config.speed;
            this.lastTime = new Date().getTime();
          }
          judge() {
            const currentTime = new Date().getTime();
            if (currentTime - this.lastTime > this.speed) {
              this.frameIndex++;
              if (this.frameIndex === 4) {
                state = RUNNING;
              }
              this.lastTime = currentTime;
            }
          }
          paint(context) {
            context.drawImage(this.frame[this.frameIndex], this.x, this.y);
          }
        }
  
  
        class Main {
          //一下全为全局变量或方法 (全局的!!)
          //初始化一个天空实例
          //主启动方法
          maingame() {
            const sky = new Sky(SKY);
            //初始化一个飞机界面加载实例
            const loading = new Loading(LOADING);
            //初始化一个英雄实例 英雄是会变的
            let hero = new Hero(HERO);
            //该变量中有所有的敌机实例
            let enemies = [];
            //该变量中存放所有的奖励实例
  
            let awards = [];
            //敌机产生的速率
            let ENEMY_CREATE_INTERVAL = 800;
            let ENEMY_LASTTIME = new Date().getTime();
  
            function stateControl() {
              //为canvas绑定一个点击事件 且他如果是START状态的时候需要修改成STARTING状态
              canvas.addEventListener("click", () => {
                if (state === START) {
                  state = STARTING;
                }
              });
              // 为canvas绑定一个鼠标移动事件 鼠标正好在飞机图片的正中心
              canvas.addEventListener("mousemove", (e) => {
                let x = e.offsetX;
                let y = e.offsetY;
                hero.x = x - hero.width / 2;
                hero.y = y - hero.height / 2;
              });
              // 为canvas绑定一个鼠标离开事件 鼠标离开时 RUNNING -> PAUSE
              canvas.addEventListener("mouseleave", () => {
                if (state === RUNNING) {
                  state = PAUSE;
                }
              });
              // 为canvas绑定一个鼠标进入事件 鼠标进入时 PAUSE => RUNNING
              canvas.addEventListener("mouseenter", () => {
                if (state === PAUSE) {
                  state = RUNNING;
                }
              });
              //为canvas绑定一个屏幕移动触摸点事件 触碰点正好在飞机图片的正中心
              canvas.addEventListener("touchmove", (e) => {
                // let x = e.pageX;
                // let y = e.pageY;
                console.log(e);
                // let x = e.touches[0].clientX;
                // let y = e.touches[0].clinetY;
                let x = e.touches[0].pageX;
                let y = e.touches[0].pageY;
                // let x = e.touches[0].screenX;
                // let y = e.touches[0].screenY;
                let write1 = (document.body.clientWidth - 480) / 2;
                let write2 = (document.body.clientHeight - 650) / 2;
                hero.x = x - write1 - hero.width / 2;
                hero.y = y - write2 - hero.height / 2;
  
                // hero.x = x - hero.width / 2;
                // hero.y = y - hero.height / 2;
                console.log(x, y);
                console.log(document.body.clientWidth, document.body.clientHeight);
                e.preventDefault(); // 阻止屏幕滚动的默认行为
  
              })
            }
            stateControl();
            // 碰撞检测函数
            //此处的碰撞检测包括 
            //1.子弹与敌机的碰撞
            //2.英雄与敌机的碰撞
            //3.英雄与随机奖励的碰撞
            function checkHit() {
              // 遍历所有的敌机
              for (let i = 0; i < awards.length; i++) {
                //检测英雄是否碰到奖励类
                if (awards[i].hit(hero)) {
                  //当然了,这个随机奖励的样式也要删了
                  awards.splice(i, 1);
                  //清除所有的敌机
                  // for (let i = 0; i < enemies.length; i++) {
                  //   enemies.splice(i, 1);
                  // }
                  enemies.length = 0;
  
                }
              }
              for (let i = 0; i < enemies.length; i++) {
                //检测英雄是否撞到敌机
                if (enemies[i].hit(hero)) {
                  //将敌机和英雄的destory属性改为true
                  enemies[i].collide();
                  hero.collide();
                }
                for (let j = 0; j < hero.bulletList.length; j++) {
                  enemies[i].hit(hero.bulletList[j]);
                  //检测子弹是否撞到敌机
                  if (enemies[i].hit(hero.bulletList[j])) {
                    //将敌机和子弹的destory属性改为true
                    enemies[i].collide();
                    hero.bulletList[j].collide();
                  }
                }
              }
            }
            // 全局函数 隔一段时间就来初始化一架敌机/奖励
            function createComponent() {
              const currentTime = new Date().getTime();
              if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) {
                let ran = Math.floor(Math.random() * 100);
                if (ran < 55) {
                  enemies.push(new Enemy(E1));
                } else if (ran < 85 && ran > 55) {
                  enemies.push(new Enemy(E2));
                } else if (ran < 95 && ran > 85) {
                  enemies.push(new Enemy(E3));
                } else if (ran > 95) {
                  awards.push(new award(C1));
  
                }
  
                ENEMY_LASTTIME = currentTime;
              }
            }
            // 全局函数 来判断所有的子弹/敌人组件 "负责移动"
            function judgeComponent() {
              for (let i = 0; i < hero.bulletList.length; i++) {
                hero.bulletList[i].move();
              }
              for (let i = 0; i < enemies.length; i++) {
                enemies[i].move();
              }
              for (let i = 0; i < awards.length; i++) {
                awards[i].move();
              }
            }
            // 全局函数 来绘制所有的子弹/敌人组件 绘制score&life面板
            function paintComponent() {
              for (let i = 0; i < hero.bulletList.length; i++) {
                hero.bulletList[i].paint(context);
              }
              for (let i = 0; i < enemies.length; i++) {
                enemies[i].paint(context);
              }
              for (let i = 0; i < awards.length; i++) {
                awards[i].paint(context);
              }
              context.font = "20px 微软雅黑";
              context.fillStyle = "green";
              context.textAlign = "left";
              context.fillText("score: " + score, 10, 20);
              context.textAlign = "right";
              context.fillText("life: " + life, 480 - 10, 20);
              //重置样式
              context.fillStyle = "black";
              context.textAlign = "left";
            }
            // 全局函数 来销毁所有的子弹/敌人组件 销毁掉英雄
            function deleteComponent() {
              if (hero.destory) {
                life--;
                hero.destory = false;
                if (life === 0) {
                  state = END;
                } else {
                  hero = new Hero(HERO);
                }
              }
              for (let i = 0; i < hero.bulletList.length; i++) {
                if (hero.bulletList[i].outOfBounds() || hero.bulletList[i].destory) {
                  hero.bulletList.splice(i, 1);
                }
              }
              for (let i = 0; i < enemies.length; i++) {
                if (enemies[i].outOfBounds() || enemies[i].destory) {
                  enemies.splice(i, 1);
                }
              }
            }
  
            //当图片加载完毕时,需要做某些事情
            bg.addEventListener("load", () => {
              setInterval(() => {
                switch (state) {
                  case START:
                    sky.judge();
                    sky.paint(context);
                    let logo_x = (480 - copyright.naturalWidth) / 2;
                    let logo_y = (650 - copyright.naturalHeight) / 2;
                    context.drawImage(copyright, logo_x, logo_y);
                    break;
                  case STARTING:
                    sky.judge();
                    sky.paint(context);
                    loading.judge();
                    loading.paint(context);
                    break;
                  case RUNNING:
                    sky.judge();
                    sky.paint(context);
                    hero.judge();
                    hero.paint(context);
                    hero.shoot();
                    createComponent();
                    judgeComponent();
                    deleteComponent();
                    paintComponent();
                    checkHit();
                    break;
                  case PAUSE:
                    let pause_x = (480 - pause.naturalWidth) / 2;
                    let pause_y = (650 - pause.naturalHeight) / 2;
                    context.drawImage(pause, pause_x, pause_y);
                    break;
                  case END:
                    //给我的画笔设置一个字的样式
                    //后面写出来的字都是这个样式的
                    context.font = "bold 24px 微软雅黑";
                    context.textAlign = "center";
                    context.textBaseline = "middle";
                    context.fillText("GAME_OVER", 480 / 2, 650 / 2);
                    break;
                }
              }, 10);
            });
  
  
            //背景切换方法
            // function changebg() {
            //     console.log("changebg方法被触发")
            //     bg.src = "img/background.png"
            // }
          }
        }
        //
  
        //初始化一个天空类
        class Sky {
          constructor(config) {
            this.bg = config.bg;
            this.width = config.width;
            this.height = config.height;
            this.x1 = 0;
            this.y1 = 0;
            this.x2 = 0;
            this.y2 = -this.height;
            this.speed = config.speed;
            this.lastTime = new Date().getTime();
          }
          //判断方法
          judge() {
            let currentTime = new Date().getTime();
            if (currentTime - this.lastTime > this.speed) {
              this.y1++;
              this.y2++;
              this.lastTime = currentTime;
            }
            if (this.y2 === 0) {
              this.y1 = 0;
              this.y2 = -this.height;
            }
          }
          //绘图方法
          paint(context) {
            context.drawImage(this.bg, this.x1, this.y1, this.width, this.height);
            context.drawImage(this.bg, this.x2, this.y2, this.width, this.height);
          }
        }
        let main_1 = new Main()
        main_1.maingame();
      }
    }
  
  
  </script>
  
  <style>
  #stage {
    width: 480px;
    height: 650px;
    margin: 0 auto;
  }
  </style>
  
  
Helloworld.vue

 

一看,738行,这,没人想维护的,复制粘贴都嫌累

(再看一眼就要爆炸)

 

我们要实现一个这样的效果(事实上也实现了)

<template>
  <div>
    <div ref="stage"></div>
  </div>
</template>

<script>
import { canvas, main_1 } from "panghu-planebattle-modular"

export default {
  mounted() {
    this.$refs.stage.appendChild(canvas);
    main_1.maingame();
  }
}
</script>

<style>
#stage {
  width: 480px;
  height: 650px;
  margin: 0 auto;
}
</style>

 

而事实上,就是三行代码:

//从包中导入canvas,main_1
import { canvas, main_1 } from "panghu-planebattle-modular"

//dom操作添加canvas
this.$refs.stage.appendChild(canvas);
 
//调用main_1的maingame方法
main_1.maingame();

 

让使用者操作的部分由738行代码变成3行代码

 

2.开始分包

将程序主要分成下面几个需要处理的部分

1、静态的:图片,图片地址,配置项

2、六个小类:Enemy、Hero、Loading、Sky、Award、Bullet

3、主启动类:Main

4、全局方法,全局变量

5、入口(对外导出的对象)

 

项目目录结构如下:

 

 

 

1.图片

 

2.配置项

首先是配置项config.js

我们将所有的配置项文件都放在这里,全局变量也放在这里

 如果将来我们要调参数,比如说,改改图片,改改子弹的速度之类,就直接在这个文件里改就行了

 

3.其中六个小类,我把他们"独立"分开

比如Bullet(子弹类)

//初始化一个子弹类
class Bullet {
  constructor(config, x, y) {
    this.img = config.img;
    this.width = config.width;
    this.height = config.height;
    this.x = x;
    this.y = y;
    this.destory = false;
  }
  //子弹绘制方法
  paint(context) {
    context.drawImage(this.img, this.x, this.y);
  }
  //移动子弹 this.y--
  move() {
    this.y -= 8;
  }
  outOfBounds() {
    //如果返回的是真的话 那么我们应该销毁掉这个子弹
    return this.y < -this.height;
  }
  collide() {
    //让这颗子弹变成可销毁状态
    this.destory = true;
  }
}

export default Bullet 

 

这里需要提一嘴的是,类的导出必须带 default,否则会报错

export default Bullet 

 

 

4.主启动类main

我们将所有曾经的全局方法,还有定时器都封装到这个类中

最后新建一个实例,并导出

  1 import Enemy from "./enemy"
  2 import Hero from "./hero"
  3 import Loading from "./loading"
  4 import Sky from "./sky"
  5 import Award from "./award"
  6 
  7 import { START, STARTING, RUNNING, PAUSE, END } from "./config"
  8 import { SKY, LOADING, HERO, E1, E2, E3, C1 } from "./config"
  9 import { bg, copyright, pause } from "./config"
 10 import { canvas, context } from "./config"
 11 
 12 class Main {
 13     //以下全为全局变量或方法 (全局的!!)
 14     //初始化一个天空实例
 15     //主启动方法
 16     maingame() {
 17         const sky = new Sky(SKY);
 18         //初始化一个飞机界面加载实例
 19         const loading = new Loading(LOADING);
 20         //初始化一个英雄实例 英雄是会变的
 21         let hero = new Hero(HERO);
 22         //该变量中有所有的敌机实例
 23         let enemies = [];
 24         //该变量中存放所有的奖励实例
 25         let awards = [];
 26         //敌机产生的速率
 27         let ENEMY_CREATE_INTERVAL = 800;
 28         let ENEMY_LASTTIME = new Date().getTime();
 29 
 30         function stateControl() {
 31             //为canvas绑定一个点击事件 且他如果是START状态的时候需要修改成STARTING状态
 32             canvas.addEventListener("click", () => {
 33                 if (state === START) {
 34                     state = STARTING;
 35                 }
 36             });
 37             // 为canvas绑定一个鼠标移动事件 鼠标正好在飞机图片的正中心
 38             canvas.addEventListener("mousemove", (e) => {
 39                 let x = e.offsetX;
 40                 let y = e.offsetY;
 41                 hero.x = x - hero.width / 2;
 42                 hero.y = y - hero.height / 2;
 43             });
 44             // 为canvas绑定一个鼠标离开事件 鼠标离开时 RUNNING -> PAUSE
 45             canvas.addEventListener("mouseleave", () => {
 46                 if (state === RUNNING) {
 47                     state = PAUSE;
 48                 }
 49             });
 50             // 为canvas绑定一个鼠标进入事件 鼠标进入时 PAUSE => RUNNING
 51             canvas.addEventListener("mouseenter", () => {
 52                 if (state === PAUSE) {
 53                     state = RUNNING;
 54                 }
 55             });
 56             //为canvas绑定一个屏幕移动触摸点事件 触碰点正好在飞机图片的正中心
 57             canvas.addEventListener("touchmove", (e) => {
 58                 // let x = e.pageX;
 59                 // let y = e.pageY;
 60                 console.log(e);
 61                 // let x = e.touches[0].clientX;
 62                 // let y = e.touches[0].clinetY;
 63                 let x = e.touches[0].pageX;
 64                 let y = e.touches[0].pageY;
 65                 // let x = e.touches[0].screenX;
 66                 // let y = e.touches[0].screenY;
 67                 let write1 = (document.body.clientWidth - 480) / 2;
 68                 let write2 = (document.body.clientHeight - 650) / 2;
 69                 hero.x = x - write1 - hero.width / 2;
 70                 hero.y = y - write2 - hero.height / 2;
 71 
 72                 // hero.x = x - hero.width / 2;
 73                 // hero.y = y - hero.height / 2;
 74                 console.log(x, y);
 75                 console.log(document.body.clientWidth, document.body.clientHeight);
 76                 e.preventDefault(); // 阻止屏幕滚动的默认行为
 77 
 78             })
 79         }
 80         stateControl();
 81         // 碰撞检测函数
 82         //此处的碰撞检测包括 
 83         //1.子弹与敌机的碰撞
 84         //2.英雄与敌机的碰撞
 85         //3.英雄与随机奖励的碰撞
 86         function checkHit() {
 87             // 遍历所有的敌机
 88             for (let i = 0; i < awards.length; i++) {
 89                 //检测英雄是否碰到奖励类
 90                 if (awards[i].hit(hero)) {
 91                     //当然了,这个随机奖励的样式也要删了
 92                     awards.splice(i, 1);
 93                     //清除所有的敌机
 94                     // for (let i = 0; i < enemies.length; i++) {
 95                     //   enemies.splice(i, 1);
 96                     // }
 97                     enemies.length = 0;
 98 
 99                 }
100             }
101             for (let i = 0; i < enemies.length; i++) {
102                 //检测英雄是否撞到敌机
103                 if (enemies[i].hit(hero)) {
104                     //将敌机和英雄的destory属性改为true
105                     enemies[i].collide();
106                     hero.collide();
107                 }
108                 for (let j = 0; j < hero.bulletList.length; j++) {
109                     enemies[i].hit(hero.bulletList[j]);
110                     //检测子弹是否撞到敌机
111                     if (enemies[i].hit(hero.bulletList[j])) {
112                         //将敌机和子弹的destory属性改为true
113                         enemies[i].collide();
114                         hero.bulletList[j].collide();
115                     }
116                 }
117             }
118         }
119         // 全局函数 隔一段时间就来初始化一架敌机/奖励
120         function createComponent() {
121             const currentTime = new Date().getTime();
122             if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) {
123                 let ran = Math.floor(Math.random() * 100);
124                 if (ran < 55) {
125                     enemies.push(new Enemy(E1));
126                 } else if (ran < 85 && ran > 55) {
127                     enemies.push(new Enemy(E2));
128                 } else if (ran < 95 && ran > 85) {
129                     enemies.push(new Enemy(E3));
130                 } else if (ran > 95) {
131                     awards.push(new Award(C1));
132 
133                 }
134 
135                 ENEMY_LASTTIME = currentTime;
136             }
137         }
138         // 全局函数 来判断所有的子弹/敌人组件 "负责移动"
139         function judgeComponent() {
140             for (let i = 0; i < hero.bulletList.length; i++) {
141                 hero.bulletList[i].move();
142             }
143             for (let i = 0; i < enemies.length; i++) {
144                 enemies[i].move();
145             }
146             for (let i = 0; i < awards.length; i++) {
147                 awards[i].move();
148             }
149         }
150         // 全局函数 来绘制所有的子弹/敌人组件 绘制score&life面板
151         function paintComponent() {
152             for (let i = 0; i < hero.bulletList.length; i++) {
153                 hero.bulletList[i].paint(context);
154             }
155             for (let i = 0; i < enemies.length; i++) {
156                 enemies[i].paint(context);
157             }
158             for (let i = 0; i < awards.length; i++) {
159                 awards[i].paint(context);
160             }
161             context.font = "20px 微软雅黑";
162             context.fillStyle = "green";
163             context.textAlign = "left";
164             context.fillText("score: " + score, 10, 20);
165             context.textAlign = "right";
166             context.fillText("life: " + life, 480 - 10, 20);
167             //重置样式
168             context.fillStyle = "black";
169             context.textAlign = "left";
170         }
171         // 全局函数 来销毁所有的子弹/敌人组件 销毁掉英雄
172         function deleteComponent() {
173             if (hero.destory) {
174                 life--;
175                 hero.destory = false;
176                 if (life === 0) {
177                     state = END;
178                 } else {
179                     hero = new Hero(HERO);
180                 }
181             }
182             for (let i = 0; i < hero.bulletList.length; i++) {
183                 if (hero.bulletList[i].outOfBounds() || hero.bulletList[i].destory) {
184                     hero.bulletList.splice(i, 1);
185                 }
186             }
187             for (let i = 0; i < enemies.length; i++) {
188                 if (enemies[i].outOfBounds() || enemies[i].destory) {
189                     enemies.splice(i, 1);
190                 }
191             }
192         }
193 
194         //当图片加载完毕时,需要做某些事情
195         bg.addEventListener("load", () => {
196             setInterval(() => {
197                 switch (state) {
198                     case START:
199                         sky.judge();
200                         sky.paint(context);
201                         let logo_x = (480 - copyright.naturalWidth) / 2;
202                         let logo_y = (650 - copyright.naturalHeight) / 2;
203                         context.drawImage(copyright, logo_x, logo_y);
204                         break;
205                     case STARTING:
206                         sky.judge();
207                         sky.paint(context);
208                         loading.judge();
209                         loading.paint(context);
210                         break;
211                     case RUNNING:
212                         sky.judge();
213                         sky.paint(context);
214                         hero.judge();
215                         hero.paint(context);
216                         hero.shoot(context);
217                         createComponent();
218                         judgeComponent();
219                         deleteComponent();
220                         paintComponent();
221                         checkHit();
222                         break;
223                     case PAUSE:
224                         let pause_x = (480 - pause.naturalWidth) / 2;
225                         let pause_y = (650 - pause.naturalHeight) / 2;
226                         context.drawImage(pause, pause_x, pause_y);
227                         break;
228                     case END:
229                         //给我的画笔设置一个字的样式
230                         //后面写出来的字都是这个样式的
231                         context.font = "bold 24px 微软雅黑";
232                         context.textAlign = "center";
233                         context.textBaseline = "middle";
234                         context.fillText("GAME_OVER", 480 / 2, 650 / 2);
235                         break;
236                 }
237             }, 10);
238         });
239 
240 
241         //背景切换方法
242         // function changebg() {
243         //     console.log("changebg方法被触发")
244         //     bg.src = "img/background.png"
245         // }
246     }
247 }
248 export let main_1 = new Main()
249 // export default Main 
main.js

 

 

5.包的入口

首先看一眼package.json

 看main,(这个可以自己调的)

由上图可知,这个包的入口就是index.js了

 

//index.js
export { canvas } from "./config"
export { main_1 } from "./main"

config.js中的canvas

export let canvas = document.createElement('canvas');
canvas.width = 480;
canvas.height = 650;
canvas.ref = canvas;
canvas.style = "border: 1px solid red;"

main.js中的main

export let main_1 = new Main()

 

在这里,用上我们前几天学的语法(嘿嘿)

 

分包完成

 

 

与我的第一个项目(十一) :飞机大战分包完成(简单阐述分包思路以及过程)相似的内容:

我的第一个项目(十一) :飞机大战分包完成(简单阐述分包思路以及过程)

好家伙, 代码已开源 Git: https://gitee.com/tang-and-han-dynasties/panghu-planebattle-esm.git NPM: panghu-planebattle-esm - npm (npmjs.com) 现在,比如说,我用Vue写好了个人博客主

我的第一个项目(十) :处理全局变量(解决模块化后变量无法获取的问题)

好家伙, 飞机大战分包分的差不多了, 但是又出现了问题: 文件目录如下: 然而关于变量 helloworld.vue完整代码

我的第一个项目(十二) :分数和生命值的更新(后端增删查改的"改")

好家伙,写后端,这多是一件美逝. 关于这个项目的代码前面的博客有写 我的第一个独立项目 - 随笔分类 - 养肥胖虎 - 博客园 (cnblogs.com) 现在,我们登陆进去了,我开始和敌人战斗,诶,打到一百分了,我现在要把这个分数保存起来 1.前端先把测试样例写好 随便写一个测试样例

我的第一个项目(十三) :组件间传值的一些方案(vuex,eventbus,localStorage)

好家伙, 先说一下我的需求,我要组件间传值 1.eventBus 前端兄弟组件传值eventbus无法使用 不报错也不触发,就很奇怪 //eventBus.js import Vue from "vue"; export default new Vue(); //Mylogin.vue

我的第一个项目(十四) :完成数据保存功能(前端,增查改接口)

好家伙,天天拖,终于写完了 代码已开源(Gitee) PH-planewar: 个人开发的全栈小游戏 前端:vue2 + element-ui 后端: Springboot + mybatis-plus 数据库: mysql 目前实现功能: 1.注册登陆 2.游戏数据保存 3.游戏运行 (gitee

我的第一个项目(十五) :完成数据保存功能(后端,改update)

好家伙, 代码已开源(Gitee) PH-planewar: 个人开发的全栈小游戏 前端:vue2 + element-ui 后端: Springboot + mybatis-plus 数据库: mysql 目前实现功能: 1.注册登陆 2.游戏数据保存 3.游戏运行 (gitee.com) 后端这

前后端分离项目(十):实现"改"功能(前后端)

好家伙,本篇介绍如何实现"改" 我们先来看看效果吧 (这可不是假数据哟,这是真数据哟) (忘记录鼠标了,这里是点了一下刷新) First Of All 我们依旧先来理一下思路: 首先在"管理"页面中,我能看到所有的书本信息, 随后,在每一个信息后都有对应的"修改按钮" 当我点击这个按钮时,我要①拿到

我的第一个项目(二):使用Vue做一个登录注册界面

好家伙, 顶不住了,太多的bug, 本来是想把背景用canvas做成动态的,但是,出现了各种问题 为了不耽误进度,我们先把一个简单的登录注册界面做出来 来看看效果: (看上去还不错) 本界面使用Vue2(新建项目的时候记得把less勾上,项目里有用到) 1.项目目录: 2.MyLogin.vue组件

我的第一个项目(三):注册登陆功能(后端)

好家伙,前端出了点bug 我们来搞定后端先: 后端我们用的框架是Spring boot 数据库:MySQl 代码已开源,连接在最后 新建项目: 只点Java Web 项目目录如下: 1.首先,我们在pom.xml文件中导入第三方包: web服务,mysql连接驱动等一系列包 pom.xml文件: <

我的第一个项目(四):(前端)发送请求以及表单校验

好家伙,本篇将继续完善前端界面 效果展示: 1.注册登陆 (后端已启动) 2.注册表单验证 (前端实现的表单验证) 在此之前: 我的第一个项目(二):使用Vue做一个登录注册界面 - 养肥胖虎 - 博客园 (cnblogs.com) 后端部分: 我的第一个项目(三):注册登陆功能(后端) - 养肥胖