我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何用VUE和Canvas实现雷霆战机打字类小游戏

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何用VUE和Canvas实现雷霆战机打字类小游戏

今天就来实现一个雷霆战机打字游戏,玩法很简单,每一个“敌人”都是一些英文单词,键盘正确打出单词的字母,飞机就会发射一个个子弹消灭“敌人”,每次需要击毙当前“敌人”后才能击毙下一个,一个比手速和单词熟练度的游戏。

首先来看看最终效果图:

emmmmmmmmmmmmm,界面UI做的很简单,先实现基本功能,再考虑高大上的UI吧。

首先依旧是来分析界面组成:

(1)固定在画面底部中间的飞机;

(2)从画面上方随机产生的敌人(单词);

(3)从飞机头部发射出去,直奔敌人而去的子弹;

(4)游戏结束后的分数显示。

这次的游戏和之前的比,运动的部分貌似更多且更复杂了。在flappy bird中,虽然管道是运动的,但是小鸟的x坐标和管道的间隔、宽度始终不变,比较容易计算边界;在弹球消砖块游戏中,木板和砖块都是相对简单或者固定的坐标,只用判定弹球的边界和砖块的触碰面积就行。在雷霆战机消单词游戏中,无论是降落的目标单词,还是飞出去的子弹,都有着各自的运动轨迹,但是子弹又要追寻着目标而去,所以存在着一个实时计算轨道的操作。

万丈高楼平地起,说了那么多,那就从最简单的开始着手吧!

1、固定在画面底部中间的飞机

这个很简单没啥好说的,这里默认飞机宽度高度为40像素单位,然后将飞机画在画面的底部中间:


drawPlane() {
      let _this = this;
      _this.ctx.save();
      _this.ctx.drawImage(
        _this.planeImg,
        _this.clientWidth / 2 - 20,
        _this.clientHeight - 20 - 40,
        40,
        40
      );
      _this.ctx.restore();
},

2、从画面上方随机产生的敌人

这里默认设置每次在画面中最多只出现3个单词靶子,靶子的y轴移动速度为1.3,靶子的半径大小为10:


const _MAX_TARGET = 3; // 画面中一次最多出现的目标

const _TARGET_CONFIG = {
  // 靶子的固定参数

  speed: 1.3,

  radius: 10

};

然后我们一开始要随机在词库数组里取出_MAX_TARGET个不重复的单词,并把剩下的词放进循环词库this.wordsPool中去:


generateWord(number) {
      // 从池子里随机挑选一个词,不与已显示的词重复
      let arr = [];
      for (let i = 0; i < number; i++) {
        let random = Math.floor(Math.random() * this.wordsPool.length);
        arr.push(this.wordsPool[random]);
        this.wordsPool.splice(random, 1);
      }
      return arr;
},
generateTarget() {
      // 随机生成目标
      let _this = this;
      let length = _this.targetArr.length;
      if (length < _MAX_TARGET) {
        let txtArr = _this.generateWord(_MAX_TARGET - length);
        for (let i = 0; i < _MAX_TARGET - length; i++) {
          _this.targetArr.push({
            x: _this.getRandomInt(
              _TARGET_CONFIG.radius,
              _this.clientWidth - _TARGET_CONFIG.radius
            ),
            y: _TARGET_CONFIG.radius * 2,
            txt: txtArr[i],
            typeIndex: -1,
            hitIndex: -1,
            dx: (_TARGET_CONFIG.speed * Math.random().toFixed(1)) / 2,
            dy: _TARGET_CONFIG.speed * Math.random().toFixed(1),
            rotate: 0
          });
        }
      }
}

可以看出,this.targetArr是存放目标对象的数组:

一个初始的目标有随机分布在画面宽度的x;

y轴值为直径;

txt记录靶子代表的单词;

typeIndex记录“轰炸”这个单词时正在敲击的字符索引下标(用来分离已敲击字符和未敲击字符);

hitIndex记录“轰炸”这个单词时子弹轰炸的索引下标(因为子弹真正轰炸掉目标是在打完单词之后,毕竟子弹有飞行时间,所以通过hitIndex来判断子弹什么时候被击碎消失);

dx是靶子每帧在x轴偏移距离;

dy是靶子每帧在y轴偏移距离;

rotate设置靶子自转角度。

好了,生成了3个靶子,我们就让靶子先从上往下动起来吧:


drawTarget() {
      // 逐帧画目标
      let _this = this;
      _this.targetArr.forEach((item, index) => {
        _this.ctx.save();
        _this.ctx.translate(item.x, item.y); //设置旋转的中心点
        _this.ctx.beginPath();
        _this.ctx.font = "14px Arial";
        if (
          index === _this.currentIndex ||
          item.typeIndex === item.txt.length - 1
        ) {
          _this.drawText(
            item.txt.substring(0, item.typeIndex + 1),
            -item.txt.length * 3,
            _TARGET_CONFIG.radius * 2,
            "gray"
          );
          let width = _this.ctx.measureText(
            item.txt.substring(0, item.typeIndex + 1)
          ).width; // 获取已敲击文字宽度
          _this.drawText(
            item.txt.substring(item.typeIndex + 1, item.txt.length),
            -item.txt.length * 3 + width,
            _TARGET_CONFIG.radius * 2,
            "red"
          );
        } else {
          _this.drawText(
            item.txt,
            -item.txt.length * 3,
            _TARGET_CONFIG.radius * 2,
            "yellow"
          );
        }
 
        _this.ctx.closePath();
        _this.ctx.rotate((item.rotate * Math.PI) / 180);
        _this.ctx.drawImage(
          _this.targetImg,
          -1 * _TARGET_CONFIG.radius,
          -1 * _TARGET_CONFIG.radius,
          _TARGET_CONFIG.radius * 2,
          _TARGET_CONFIG.radius * 2
        );
        _this.ctx.restore();
        item.y += item.dy;
        item.x += item.dx;
        if (item.x < 0 || item.x > _this.clientWidth) {
          item.dx *= -1;
        }
        if (item.y > _this.clientHeight - _TARGET_CONFIG.radius * 2) {
          // 碰到底部了
          _this.gameOver = true;
        }
        // 旋转
        item.rotate++;
      });
}

这一步画靶子没有什么特别的,随机得增加dx和dy,在碰到左右边缘时反弹。主要一点就是单词的绘制,通过typeIndex将单词一分为二,敲击过的字符置为灰色,然后通过measureText获取到敲击字符的宽度从而设置未敲击字符的x轴偏移量,将未敲击的字符设置为红色,提示玩家这个单词是正在攻击中的目标。

3、从飞机头部发射出去,直奔敌人而去的子弹

子弹是这个游戏的关键部分,在绘制子弹的时候要考虑哪些方面呢?

(1)目标一直是运动的,发射出去的子弹要一直“追踪”目标,所以路径是动态变化的;

(2)一个目标需要被若干个子弹消灭掉,所以什么时候子弹才从画面中抹去;

(3)当一个目标单词被敲击完了后,下一批子弹就要朝向下一个目标射击,所以子弹的路径是单独的;

(4)如何绘制出子弹的拖尾效果;

(5)如果锁定正在敲击的目标单词,使玩家敲完当前单词才能去击破下一个单词

这里先设置几个变量:

bulletArr: [], // 存放子弹对象

currentIndex: -1  //当前锁定的目标在targetArr中的索引

首先我们先来写一下键盘按下时要触发的函数:


handleKeyPress(key) {
      // 键盘按下,判断当前目标
      let _this = this;
      if (_this.currentIndex === -1) {
        // 当前没有在射击的目标
        let index = _this.targetArr.findIndex(item => {
          return item.txt.indexOf(key) === 0;
        });
        if (index !== -1) {
          _this.currentIndex = index;
          _this.targetArr[index].typeIndex = 0;
          _this.createBullet(index);
        }
      } else {
        // 已有目标正在被射击
        if (
          key ===
          _this.targetArr[_this.currentIndex].txt.split("")[
            _this.targetArr[_this.currentIndex].typeIndex + 1
          ]
        ) {
          // 获取到目标对象
          _this.targetArr[_this.currentIndex].typeIndex++;
          _this.createBullet(_this.currentIndex);
 
          if (
            _this.targetArr[_this.currentIndex].typeIndex ===
            _this.targetArr[_this.currentIndex].txt.length - 1
          ) {
            // 这个目标已经别射击完毕
            _this.currentIndex = -1;
          }
        }
      }
},
// 发射一个子弹
    createBullet(index) {
      let _this = this;
      this.bulletArr.push({
        dx: 1,
        dy: 4,
        x: _this.clientWidth / 2,
        y: _this.clientHeight - 60,
        targetIndex: index
      });
}

这个函数做的事情很明确,拿到当前键盘按下的字符,如果currentIndex ===-1,证明没有正在被攻击的靶子,所以就去靶子数组里看,哪个单词的首字母等于该字符,则设置currentIndex为该单词的索引,并发射一个子弹;如果已经有正在被攻击的靶子,则看还未敲击的单词的第一个字符是否符合,若符合,则增加该靶子对象的typeIndex,并发射一个子弹,若当前靶子已敲击完毕,则重置currentIndex为-1。

接下来就是画子弹咯:


drawBullet() {
      // 逐帧画子弹
      let _this = this;
      // 判断子弹是否已经击中目标
      if (_this.bulletArr.length === 0) {
        return;
      }
      _this.bulletArr = _this.bulletArr.filter(_this.firedTarget);
      _this.bulletArr.forEach(item => {
        let targetX = _this.targetArr[item.targetIndex].x;
        let targetY = _this.targetArr[item.targetIndex].y;
        let k =
          (_this.clientHeight - 60 - targetY) /
          (_this.clientWidth / 2 - targetX); // 飞机头和目标的斜率
        let b = targetY - k * targetX; // 常量b
        item.y = item.y - bullet.dy; // y轴偏移一个单位
        item.x = (item.y - b) / k;
        for (let i = 0; i < 15; i++) {
          // 画出拖尾效果
          _this.ctx.beginPath();
          _this.ctx.arc(
            (item.y + i * 1.8 - b) / k,
            item.y + i * 1.8,
            4 - 0.2 * i,
            0,
            2 * Math.PI
          );
          _this.ctx.fillStyle = `rgba(193,255,255,${1 - 0.08 * i})`;
          _this.ctx.fill();
          _this.ctx.closePath();
        }
      });
},
firedTarget(item) {
      // 判断是否击中目标
      let _this = this;
      if (
        item.x > _this.targetArr[item.targetIndex].x - _TARGET_CONFIG.radius &&
        item.x < _this.targetArr[item.targetIndex].x + _TARGET_CONFIG.radius &&
        item.y > _this.targetArr[item.targetIndex].y - _TARGET_CONFIG.radius &&
        item.y < _this.targetArr[item.targetIndex].y + _TARGET_CONFIG.radius
      ) {
        // 子弹击中了目标
        let arrIndex = item.targetIndex;
        _this.targetArr[arrIndex].hitIndex++;
        if (
          _this.targetArr[arrIndex].txt.length - 1 ===
          _this.targetArr[arrIndex].hitIndex
        ) {
          // 所有子弹全部击中了目标
          let word = _this.targetArr[arrIndex].txt;
          _this.targetArr[arrIndex] = {
            // 生成新的目标
            x: _this.getRandomInt(
              _TARGET_CONFIG.radius,
              _this.clientWidth - _TARGET_CONFIG.radius
            ),
            y: _TARGET_CONFIG.radius * 2,
            txt: _this.generateWord(1)[0],
            typeIndex: -1,
            hitIndex: -1,
            dx: (_TARGET_CONFIG.speed * Math.random().toFixed(1)) / 2,
            dy: _TARGET_CONFIG.speed * Math.random().toFixed(1),
            rotate: 0
          };
          _this.wordsPool.push(word); // 被击中的目标词重回池子里
          _this.score++;
        }
        return false;
      } else {
        return true;
      }
}

其实也很简单,我们在子弹对象中用targetIndex来记录该子弹所攻击的目标索引,然后就是一个y = kx+b的解方程得到飞机头部(子弹出发点)和靶子的轨道函数,计算出每一帧下每个子弹的移动坐标,就可以画出子弹了;

拖尾效果就是沿轨道y轴增长方向画出若干个透明度和半径逐渐变小的圆,就能实现拖尾效果了;

在firedTarget()函数中,用来过滤出已击中靶子的子弹,为了不影响还在被攻击的其他靶子在targetArr中的索引,不用splice删除,而是直接重置被消灭靶子的值,从wordPool中选出新的词,并把当前击碎的词重新丢回池子里,从而保证画面中不会出现重复的靶子。

4、游戏结束后的得分文字效果

游戏结束就是有目标靶子触碰到了底部就结束了。

这里其实是个彩蛋了,怎么样可以用canvas画出这种闪烁且有光晕的文字呢?切换颜色并且叠buff就行了,不是,叠轮廓stroke就行了


drawGameOver() {
      let _this = this;
      //保存上下文对象的状态
      _this.ctx.save();
      _this.ctx.font = "34px Arial";
      _this.ctx.strokeStyle = _this.colors[0];
      _this.ctx.lineWidth = 2;
      //光晕
      _this.ctx.shadowColor = "#FFFFE0";
      let txt = "游戏结束,得分:" + _this.score;
      let width = _this.ctx.measureText(txt).width;
      for (let i = 60; i > 3; i -= 2) {
        _this.ctx.shadowBlur = i;
        _this.ctx.strokeText(txt, _this.clientWidth / 2 - width / 2, 300);
      }
      _this.ctx.restore();
      _this.colors.reverse();
}

好了好了,做到这里就是个还算完整的小游戏了,只是UI略显粗糙,如果想做到真正雷霆战机那么酷炫带爆炸的效果那还需要很多素材和canvas的绘制。

canvas很强大很好玩,只要脑洞够大,这张画布上你想画啥就可以画啥~

老规矩,po上vue的完整代码供大家参考学习:


<template>
  <div class="type-game">
    <canvas id="type" width="400" height="600"></canvas>
  </div>
</template>
 
<script>
const _MAX_TARGET = 3; // 画面中一次最多出现的目标
const _TARGET_CONFIG = {
  // 靶子的固定参数
  speed: 1.3,
  radius: 10
};
const _DICTIONARY = ["apple", "orange", "blue", "green", "red", "current"];
export default {
  name: "TypeGame",
  data() {
    return {
      ctx: null,
      clientWidth: 0,
      clientHeight: 0,
      bulletArr: [], // 屏幕中的子弹
      targetArr: [], // 存放当前目标
      targetImg: null,
      planeImg: null,
      currentIndex: -1,
      wordsPool: [],
      score: 0,
      gameOver: false,
      colors: ["#FFFF00", "#FF6666"]
    };
  },
  mounted() {
    let _this = this;
    _this.wordsPool = _DICTIONARY.concat([]);
    let container = document.getElementById("type");
    _this.clientWidth = container.width;
    _this.clientHeight = container.height;
    _this.ctx = container.getContext("2d");
    _this.targetImg = new Image();
    _this.targetImg.class="lazy" data-src = require("@/assets/img/target.png");
 
    _this.planeImg = new Image();
    _this.planeImg.class="lazy" data-src = require("@/assets/img/plane.png");
 
    document.onkeydown = function(e) {
      let key = window.event.keyCode;
      if (key >= 65 && key <= 90) {
        _this.handleKeyPress(String.fromCharCode(key).toLowerCase());
      }
    };
 
    _this.targetImg.onload = function() {
      _this.generateTarget();
      (function animloop() {
        if (!_this.gameOver) {
          _this.drawAll();
        } else {
          _this.drawGameOver();
        }
        window.requestAnimationFrame(animloop);
      })();
    };
  },
  methods: {
    drawGameOver() {
      let _this = this;
      //保存上下文对象的状态
      _this.ctx.save();
      _this.ctx.font = "34px Arial";
      _this.ctx.strokeStyle = _this.colors[0];
      _this.ctx.lineWidth = 2;
      //光晕
      _this.ctx.shadowColor = "#FFFFE0";
      let txt = "游戏结束,得分:" + _this.score;
      let width = _this.ctx.measureText(txt).width;
      for (let i = 60; i > 3; i -= 2) {
        _this.ctx.shadowBlur = i;
        _this.ctx.strokeText(txt, _this.clientWidth / 2 - width / 2, 300);
      }
      _this.ctx.restore();
      _this.colors.reverse();
    },
    drawAll() {
      let _this = this;
      _this.ctx.clearRect(0, 0, _this.clientWidth, _this.clientHeight);
      _this.drawPlane(0);
      _this.drawBullet();
      _this.drawTarget();
      _this.drawScore();
    },
    drawPlane() {
      let _this = this;
      _this.ctx.save();
      _this.ctx.drawImage(
        _this.planeImg,
        _this.clientWidth / 2 - 20,
        _this.clientHeight - 20 - 40,
        40,
        40
      );
      _this.ctx.restore();
    },
    generateWord(number) {
      // 从池子里随机挑选一个词,不与已显示的词重复
      let arr = [];
      for (let i = 0; i < number; i++) {
        let random = Math.floor(Math.random() * this.wordsPool.length);
        arr.push(this.wordsPool[random]);
        this.wordsPool.splice(random, 1);
      }
      return arr;
    },
    generateTarget() {
      // 随机生成目标
      let _this = this;
      let length = _this.targetArr.length;
      if (length < _MAX_TARGET) {
        let txtArr = _this.generateWord(_MAX_TARGET - length);
        for (let i = 0; i < _MAX_TARGET - length; i++) {
          _this.targetArr.push({
            x: _this.getRandomInt(
              _TARGET_CONFIG.radius,
              _this.clientWidth - _TARGET_CONFIG.radius
            ),
            y: _TARGET_CONFIG.radius * 2,
            txt: txtArr[i],
            typeIndex: -1,
            hitIndex: -1,
            dx: (_TARGET_CONFIG.speed * Math.random().toFixed(1)) / 2,
            dy: _TARGET_CONFIG.speed * Math.random().toFixed(1),
            rotate: 0
          });
        }
      }
    },
    getRandomInt(n, m) {
      return Math.floor(Math.random() * (m - n + 1)) + n;
    },
    drawText(txt, x, y, color) {
      let _this = this;
      _this.ctx.fillStyle = color;
      _this.ctx.fillText(txt, x, y);
    },
    drawScore() {
      // 分数
      this.drawText("分数:" + this.score, 10, this.clientHeight - 10, "#fff");
    },
    drawTarget() {
      // 逐帧画目标
      let _this = this;
      _this.targetArr.forEach((item, index) => {
        _this.ctx.save();
        _this.ctx.translate(item.x, item.y); //设置旋转的中心点
        _this.ctx.beginPath();
        _this.ctx.font = "14px Arial";
        if (
          index === _this.currentIndex ||
          item.typeIndex === item.txt.length - 1
        ) {
          _this.drawText(
            item.txt.substring(0, item.typeIndex + 1),
            -item.txt.length * 3,
            _TARGET_CONFIG.radius * 2,
            "gray"
          );
          let width = _this.ctx.measureText(
            item.txt.substring(0, item.typeIndex + 1)
          ).width; // 获取已敲击文字宽度
          _this.drawText(
            item.txt.substring(item.typeIndex + 1, item.txt.length),
            -item.txt.length * 3 + width,
            _TARGET_CONFIG.radius * 2,
            "red"
          );
        } else {
          _this.drawText(
            item.txt,
            -item.txt.length * 3,
            _TARGET_CONFIG.radius * 2,
            "yellow"
          );
        }
 
        _this.ctx.closePath();
        _this.ctx.rotate((item.rotate * Math.PI) / 180);
        _this.ctx.drawImage(
          _this.targetImg,
          -1 * _TARGET_CONFIG.radius,
          -1 * _TARGET_CONFIG.radius,
          _TARGET_CONFIG.radius * 2,
          _TARGET_CONFIG.radius * 2
        );
        _this.ctx.restore();
        item.y += item.dy;
        item.x += item.dx;
        if (item.x < 0 || item.x > _this.clientWidth) {
          item.dx *= -1;
        }
        if (item.y > _this.clientHeight - _TARGET_CONFIG.radius * 2) {
          // 碰到底部了
          _this.gameOver = true;
        }
        // 旋转
        item.rotate++;
      });
    },
    handleKeyPress(key) {
      // 键盘按下,判断当前目标
      let _this = this;
      if (_this.currentIndex === -1) {
        // 当前没有在射击的目标
        let index = _this.targetArr.findIndex(item => {
          return item.txt.indexOf(key) === 0;
        });
        if (index !== -1) {
          _this.currentIndex = index;
          _this.targetArr[index].typeIndex = 0;
          _this.createBullet(index);
        }
      } else {
        // 已有目标正在被射击
        if (
          key ===
          _this.targetArr[_this.currentIndex].txt.split("")[
            _this.targetArr[_this.currentIndex].typeIndex + 1
          ]
        ) {
          // 获取到目标对象
          _this.targetArr[_this.currentIndex].typeIndex++;
          _this.createBullet(_this.currentIndex);
 
          if (
            _this.targetArr[_this.currentIndex].typeIndex ===
            _this.targetArr[_this.currentIndex].txt.length - 1
          ) {
            // 这个目标已经别射击完毕
            _this.currentIndex = -1;
          }
        }
      }
    },
    // 发射一个子弹
    createBullet(index) {
      let _this = this;
      this.bulletArr.push({
        dx: 1,
        dy: 4,
        x: _this.clientWidth / 2,
        y: _this.clientHeight - 60,
        targetIndex: index
      });
    },
    firedTarget(item) {
      // 判断是否击中目标
      let _this = this;
      if (
        item.x > _this.targetArr[item.targetIndex].x - _TARGET_CONFIG.radius &&
        item.x < _this.targetArr[item.targetIndex].x + _TARGET_CONFIG.radius &&
        item.y > _this.targetArr[item.targetIndex].y - _TARGET_CONFIG.radius &&
        item.y < _this.targetArr[item.targetIndex].y + _TARGET_CONFIG.radius
      ) {
        // 子弹击中了目标
        let arrIndex = item.targetIndex;
        _this.targetArr[arrIndex].hitIndex++;
        if (
          _this.targetArr[arrIndex].txt.length - 1 ===
          _this.targetArr[arrIndex].hitIndex
        ) {
          // 所有子弹全部击中了目标
          let word = _this.targetArr[arrIndex].txt;
          _this.targetArr[arrIndex] = {
            // 生成新的目标
            x: _this.getRandomInt(
              _TARGET_CONFIG.radius,
              _this.clientWidth - _TARGET_CONFIG.radius
            ),
            y: _TARGET_CONFIG.radius * 2,
            txt: _this.generateWord(1)[0],
            typeIndex: -1,
            hitIndex: -1,
            dx: (_TARGET_CONFIG.speed * Math.random().toFixed(1)) / 2,
            dy: _TARGET_CONFIG.speed * Math.random().toFixed(1),
            rotate: 0
          };
          _this.wordsPool.push(word); // 被击中的目标词重回池子里
          _this.score++;
        }
        return false;
      } else {
        return true;
      }
    },
    drawBullet() {
      // 逐帧画子弹
      let _this = this;
      // 判断子弹是否已经击中目标
      if (_this.bulletArr.length === 0) {
        return;
      }
      _this.bulletArr = _this.bulletArr.filter(_this.firedTarget);
      _this.bulletArr.forEach(item => {
        let targetX = _this.targetArr[item.targetIndex].x;
        let targetY = _this.targetArr[item.targetIndex].y;
        let k =
          (_this.clientHeight - 60 - targetY) /
          (_this.clientWidth / 2 - targetX); // 飞机头和目标的斜率
        let b = targetY - k * targetX; // 常量b
        item.y = item.y - 4; // y轴偏移一个单位
        item.x = (item.y - b) / k;
        for (let i = 0; i < 15; i++) {
          // 画出拖尾效果
          _this.ctx.beginPath();
          _this.ctx.arc(
            (item.y + i * 1.8 - b) / k,
            item.y + i * 1.8,
            4 - 0.2 * i,
            0,
            2 * Math.PI
          );
          _this.ctx.fillStyle = `rgba(193,255,255,${1 - 0.08 * i})`;
          _this.ctx.fill();
          _this.ctx.closePath();
        }
      });
    }
  }
};
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.type-game {
  #type {
    background: #2a4546;
  }
}
</style>

以上就是如何用VUE和Canvas实现雷霆战机打字类小游戏的详细内容,更多关于VUE雷霆战机小游戏的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何用VUE和Canvas实现雷霆战机打字类小游戏

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何使用原生JS实现飞机大战小游戏

小编给大家分享一下如何使用原生JS实现飞机大战小游戏,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!本文实例为大家分享了JS实现飞机大战小游戏的具体代码,供大家参考,具体内容如下 </div></div><div class="articleBottom"><span class="date" style="float: right;">2023-06-15</span></div><div class="clearboth"></div></div></div></div><div class="hotFlag"><h4>热门标签</h4><div class="flagBox"><a title="Linux" href="/tag/Linux/">Linux(148)</a><a title="PHP" href="/tag/PHP/">PHP(127)</a><a title="Java" href="/tag/Java/">Java(102)</a><a title="正则表达式" href="/tag/正则表达式/">正则表达式(101)</a><a title="JavaScript" href="/tag/JavaScript/">JavaScript(69)</a><a title="最佳实践" href="/tag/最佳实践/">最佳实践(67)</a><a title="jQuery" href="/tag/jQuery/">jQuery(44)</a><a title="MySQL" href="/tag/MySQL/">MySQL(39)</a><a title="Docker" href="/tag/Docker/">Docker(37)</a><a title="C语言" href="/tag/C语言/">C语言(36)</a><a title="性能优化" href="/tag/性能优化/">性能优化(34)</a><a title="Python" href="/tag/Python/">Python(34)</a><a title="XML" href="/tag/XML/">XML(28)</a><a title="string" href="/tag/string/">string(27)</a><a title="第三方库" href="/tag/第三方库/">第三方库(23)</a><a title="回调函数" href="/tag/回调函数/">回调函数(23)</a><a title="ZIP" href="/tag/ZIP/">ZIP(22)</a><a title="数组" href="/tag/数组/">数组(22)</a><a title="可扩展性" href="/tag/可扩展性/">可扩展性(22)</a><a title="字符串比较" href="/tag/字符串比较/">字符串比较(21)</a><a title="find" href="/tag/find/">find(20)</a><a title="RPM" href="/tag/RPM/">RPM(20)</a><a title="Go" href="/tag/Go/">Go(20)</a><a title="grep" href="/tag/grep/">grep(19)</a><a title="ASP.NETCore" href="/tag/ASP.NETCore/">ASP.NETCore(19)</a><a title="XML解析器" href="/tag/XML解析器/">XML解析器(19)</a><a title="事件" href="/tag/事件/">事件(19)</a><a title="事件处理程序" href="/tag/事件处理程序/">事件处理程序(19)</a><a title="StringBuilder" href="/tag/StringBuilder/">StringBuilder(18)</a><a title="Nginx" href="/tag/Nginx/">Nginx(18)</a></div></div></div><div class="main_right fr"><div class="artHotSearch"><h4 class="title"><div class="fl"><span class="icon bg_content bg_icon_rs"></span><span class="name">编程热搜</span></div><a href="" class="fr bg_content bg_txt_rsb"></a><div class="clearboth"></div></h4><ul><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="怎么安装配置nodejs" href="https://www.528045.com/article/2f3ea96eab.html">怎么安装配置nodejs</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="/file/upload/202210/19/tixkirpxiqo.jpg" alt="怎么安装配置nodejs" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="Vue中Vue.extend()的使用及解析" href="https://www.528045.com/article/a5db73cb2f.html">Vue中Vue.extend()的使用及解析</a></span></h5><div class="clearboth"></div><div class="des">这篇文章主要介绍了Vue中Vue.extend()的使用及解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教</div></div><img class="lazy" data-type="0" data-src="https://static.528045.com/imgs/33.jpg?imageMogr2/format/webp/blur/1x0/quality/35" alt="Vue中Vue.extend()的使用及解析" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="NodeJs模拟登陆正方教务" href="https://www.528045.com/article/2ecc5d16d4.html">NodeJs模拟登陆正方教务</a></span></h5><div class="clearboth"></div><div class="des">网上已经有很多关于模拟登陆正方教务的作品了,基于 PHP,Python,Java,.Net 加上我自己尝试的NodeJs,这几门语言都可以实现模拟登陆,模拟登陆的技术点不是特别难,这里记录一下利用 Node 碰到的一些坑,以及一些我自己的解</div></div><img class="lazy" data-type="0" data-src="http://files.jb51.net/file_images/article/201704/2017428104219999.jpg" alt="NodeJs模拟登陆正方教务" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="如何让DIV水平垂直居中" href="https://www.528045.com/article/ca8452210f.html">如何让DIV水平垂直居中</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="/file/upload/202210/19/pki1d2gq0ly.jpg" alt="如何让DIV水平垂直居中" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="css中怎么设置div背景颜色" href="https://www.528045.com/article/8b624fb379.html">css中怎么设置div背景颜色</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="/file/upload/202210/19/byqxxwbwqk1.png" alt="css中怎么设置div背景颜色" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="nodejs怎么查看版本" href="https://www.528045.com/article/db3a645928.html">nodejs怎么查看版本</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="/file/upload/202210/19/m05vf0vjqbr.jpg" alt="nodejs怎么查看版本" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="html如何设置按钮文字" href="https://www.528045.com/article/2134eea2b3.html">html如何设置按钮文字</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="/file/upload/202210/19/4cmxmmrqn2p.jpg" alt="html如何设置按钮文字" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="html如何设置div的透明度" href="https://www.528045.com/article/69630be7a7.html">html如何设置div的透明度</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="/file/upload/202210/19/owhfomqa2hr.jpg" alt="html如何设置div的透明度" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="JavaScript对象(详细)" href="https://www.528045.com/article/aa5647b26d.html">JavaScript对象(详细)</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="https://static.528045.com/imgs/35.jpg?imageMogr2/format/webp/blur/1x0/quality/35" alt="JavaScript对象(详细)" /><div class="clearboth"></div></li><li class="rslist clearfix"><div class="rslist_left fl"><h5><span class="fl name"><a title="html5 mui怎么使用" href="https://www.528045.com/article/537e020a72.html">html5 mui怎么使用</a></span></h5><div class="clearboth"></div><div class="des"></div></div><img class="lazy" data-type="0" data-src="/file/upload/202210/19/jvzk30x24yu.jpg" alt="html5 mui怎么使用" /><div class="clearboth"></div></li></ul><div class="seeMore"><a href="https://www.528045.com/article/web-c5-1.html">查看更多</a></div></div><div class="artSubmit"><h4 class="title"><div class="fl"><span class="icon bg_content bg_icon_yktg"></span><span class="name">编程资源站</span></div><div class="clearboth"></div></h4><ul class="submitNav clearfix"><li class="active first">资料下载</li><li>历年试题</li></ul><div class="submitBox active"><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021年下半年软考高级信息系统项目管理师高频考点" href="https://www.528045.com/down/89bb6.html">2021年下半年软考高级信息系统项目管理师高频考点</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021下半年软考高级信息系统技术知识点记忆口诀" href="https://www.528045.com/down/7d690.html">2021下半年软考高级信息系统技术知识点记忆口诀</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021下半年软考《信息系统项目管理师》考试真题及答案" href="https://www.528045.com/down/4277a.html">2021下半年软考《信息系统项目管理师》考试真题及答案</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021下半年软考高级考试备考攻略" href="https://www.528045.com/down/9248a.html">2021下半年软考高级考试备考攻略</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021年软考高级《信息系统项目管理师》巩固练习题汇总" href="https://www.528045.com/down/fcca0.html">2021年软考高级《信息系统项目管理师》巩固练习题汇总</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021下半年软考高级信息系统项目管理师30个易考知识点汇总" href="https://www.528045.com/down/7636b.html">2021下半年软考高级信息系统项目管理师30个易考知识点汇总</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021下半年软考高级知识点这样记,还担心记不住吗" href="https://www.528045.com/down/76382.html">2021下半年软考高级知识点这样记,还担心记不住吗</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021年下半年软考高级考试重点汇总" href="https://www.528045.com/down/15f44.html">2021年下半年软考高级考试重点汇总</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021下半年软考高级信息系统项目管理师计算公式汇总" href="https://www.528045.com/down/2d560.html">2021下半年软考高级信息系统项目管理师计算公式汇总</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2021年下半年软考高级《信息系统项目管理师》模拟试题" href="https://www.528045.com/down/c0085.html">2021年下半年软考高级《信息系统项目管理师》模拟试题</a><span class="hot"><a href="https://www.528045.com/down/ziliao-c69-1.html">精选资料</a></span></h5></div><div class="seeMore"><a href="/down/">查看更多</a></div></div><div class="submitBox"><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="信息系统项目管理师选择题每日一练(2024)" href="https://www.528045.com/article/6d5f303202.html">信息系统项目管理师选择题每日一练(2024)</a><span class="hot"><a href="https://www.528045.com/article/lnst-c57-1.html">历年试题</a></span></h5></div><div class="submitItem"><span class="turn"></span><h5 class="title"><a title="2023年下半年信息系统项目管理师综合知识真题演练" href="https://www.528045.com/article/f2da969d4f.html">2023年下半年信息系统项目管理师综合知识真题演练</a><span class="hot"><a href="https://www.528045.com/article/lnst-c57-1.html">历年试题</a></span></h5></div><div class="seeMore"><a href="https://www.528045.com/article/lnst-c57-1.html">查看更多</a></div></div></div><div class="meumfie"><h4 class="title"><div class="fl"><span class="icon bg_content bg_icon_ykzl"></span><span class="name">目录</span></div><div class="clearboth"></div></h4><div class="toc"></div></div></div><div class="clearboth"></div></div><script type="text/javascript" src="/skin/static/js/toc.js"></script><div class="popUp popDelay"><div class="maskBg"></div><div class="popCon"><span class="btn btn_close"></span><div class="tip"> 本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页 </div><div class="delayMain"><div class="delaymain_left"><a href="https://www.528045.com"><img src="https://static.528045.com/skin/content_left.png?imageMogr2/format/webp/blur/1x0/quality/35"></a></div><div class="delaymain_right"><div class="news_title"><span class="fl">最新资讯</span ><a href="/" class="fr">更多</a><div class="clearboth"></div></div><ul></ul></div><div class="clearboth"></div></div><div class="bigshow"><a href="https://www.528045.com"><img src="https://static.528045.com/skin/content_bottom.png?imageMogr2/format/webp/blur/1x0/quality/35"></a></div></div></div><div class="footer"><div class="friendLink"><span class="friendTitle">友情链接</span><a href="https://www.lsjlt.com">编程网</a></div><script type="text/javascript" src="/skin/static/js/footermain.js"></script></div><div class="popCommon"><span class="mask"></span><div class="popCon"></div></div><div class="ajaxrightbtn"><div class="funBtnbox"><div class="rb_fk rb_ty br_bom"><div class="wx_mo1 _mo1"><i class="dtmuban-iconfont icon-fankui2"></i><p>反馈</p></div><a href="javascript:void(0);" onclick="Dsb('urs-form-modalfk');"><div class="_mo2 rb_gradient br_bom"><p>我要<br>反馈</p></div></a></div></div><div class="fbs fbs-gotp back2top" style="display: none;"><div class="rb_top rb_ty br_all"><div class="top_mo1 _mo1 "><i class="dtmuban-iconfont icon-zhiding"></i></div><a href="javascript:void(0);" title="返回顶部"><div class="_mo2 rb_gradient br_all"><p>返回<br>顶部</p></div></a></div></div></div><div id="urs-form-modalfk-bg" class="urs-form-modal-bg" style="display: none;"></div><div id="urs-form-modalfk" class="urs-form-modal" style="display: none;"><div class="urs-form-component plan-get-form-mini js-apply-form clearfix"><div class="urs-component-head"><div class="icon-close" onclick="Dhb('urs-form-modalfk');"><img id="close-btn" src="https://static.528045.com/skin/ic_delete_normal_24px.png"></div><p class="form-title">留言反馈</p><p></p><p class="form-line"></p></div><div class="urs-component-tab"><h5>感谢您的提交,我们服务专员将在<span>30分钟内</span>给您回复</h5><div class="get-plan-form-item"><i class="u-item-icon dtmuban-iconfont icon-fanganguihua"></i><input class="u-input js-apply-phone" type="text" id="titleb" name="titleb" value="我要反馈留言,看到请回复" placeholder="留言标题"><span id="dtitlea"></span></div><div class="get-plan-form-item"><select name="typeidb" id="typeidb" class="u-select"><option value="">请选择类型</option><option value="0">问题反馈</option><option value="1">意见建议</option><option value="2">倒卖举报</option><option value="3">不良信息</option><option value="4">其他</option></select></div><div class="get-plan-form-item"><i class="u-item-icon dtmuban-iconfont icon-yonghu"></i><input class="u-input js-apply-name" type="text" id="truenameb" name="truenameb" placeholder="您的姓名" value=""><span id="dtruenameb"></span></div><div class="get-plan-form-item"><i class="u-item-icon dtmuban-iconfont icon-liuyan"></i><input class="u-input js-apply-phone" type="text" id="emailb" name="emailb" placeholder="您的邮箱" value=""><span id="demailb"></span></div><div class="get-plan-form-item"><i class="u-item-icon dtmuban-iconfont icon-shouji"></i><input class="u-input js-apply-phone" type="text" id="telephoneb" name="telephoneb" placeholder="您的手机" value=""><span id="dtelephoneb"></span></div><div class="get-plan-form-item"><i class="u-item-icon dtmuban-iconfont icon-liuyan2"></i><textarea class="u-textarea js-apply-content" id="contentb" name="contentb" placeholder="留言说明,文字为2-100字" value=""></textarea><span id="dcontentb"></span></div><button type="submit" name="submit" class="btn btn-red" onclick="checkaguestbook();">立即提交</button></div></div></div><style> .drop_more li{ list-style: none;} </style><script type="text/javascript" src="/file/script/config.js"></script><script type="text/javascript" src="/file/script/common.js"></script><script type="text/javascript" src="/skin/static/js/commonpop.js"></script><script type="text/javascript" src="/skin/static/js/header.js"></script><script type="text/javascript" src="/skin/static/js/footer.js"></script><script type="text/javascript" src="/skin/static/js/listconcommon.js"></script><script src="/skin/static/layui/layui.js" type="text/javascript"></script><script src="/skin/static/js/custom-script.js" type="text/javascript"></script><script src="/skin/static/js/indexsms.js?v=20240108.1443"></script><script type="text/javascript"> $(function(){ var destoon_userid = 0,destoon_username = '',destoon_member = destoon_guest = ''; destoon_guest = '<div class="login-li"><a href="javascript:void(0);" lay-on="showLoginPopup" class="a_on_dl" id="dn-login">请登录</a></div><div class="login-li"><a href="javascript:void(0);" lay-on="showLoginPopup" class="a_to">免费注册</a></div>'; destoon_member += destoon_guest; $('#m52_member').html(destoon_member); }); </script></body></html><script type="text/javascript" src="/skin/static/js/contentie.js"></script>