千家信息网

怎么用Vue3+Canvas实现坦克大战游戏

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,这篇文章主要介绍了怎么用Vue3+Canvas实现坦克大战游戏的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Vue3+Canvas实现坦克大战游戏文章都会有所收获,
千家信息网最后更新 2026年11月04日怎么用Vue3+Canvas实现坦克大战游戏

这篇文章主要介绍了怎么用Vue3+Canvas实现坦克大战游戏的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Vue3+Canvas实现坦克大战游戏文章都会有所收获,下面我们一起来看看吧。

    架构搭建

    项目技术选型为 vue3、vite、less、pnpm、ts,按照vue3 官网文档来新建项目,注意:虽然我用了 vue3 实际上只是强行尝鲜,主体内容都是 js 用到的框架特性有限。

    $ pnpm create vite  -- --template vue$ cd $ pnpm install$ pnpm add -D less$ pnpm dev

    Canvas 构造函数

    游戏的核心为 canvas 画布和坦克元素,我们定义两个构造函数

    canvas 构造函数的定义参数、方法:dom、dimension 尺寸、renderTo 渲染函数、drawText 文本绘制函数、drawImageSlice 图片绘制函数

    画布绘制

    canvas 图层按照一般的游戏设计优化理念,需要为静态背景和动态元素单独用不同的 canvas 图层表示,每次更新时只需要重新绘制动态元素就好了,我抽象出一个渲染函数

    // 渲染this.renderTo = function renderTo(container_id) {  if (!is_rendered) {    let container = document.getElementById(container_id)    //画布起始坐标    dom = document.createElement('canvas') // 创造canvas画布    dom.setAttribute('class', 'canvas')    ctx = dom.getContext('2d')    dom.setAttribute('width', container.clientWidth)    dom.setAttribute('height', container.clientHeight)    // 画布尺寸    dimension = {      x: container.clientWidth,      y: container.clientHeight,    }    container.insertBefore(dom, container.firstChild) // 插入cantainer容器  }}

    文本渲染

    想要知道画布中的具体位置坐标,可以定义一个函数,当鼠标滑动时候执行来将当前位置坐标绘制出来

    this.drawText = function drawText(text, offset_left, offset_top, font) {  ctx.font = font || '25px Calibri'  ctx.fillStyle = '#fff'  ctx.fillText(text, offset_left, offset_top)}

    画布重绘前的 clear

    每次重绘前需要先擦掉整个画布

    this.clear = function clear() {  ctx.clearRect(0, 0, dimension.x, dimension.y)}

    核心:绘制函数

    坦克、子弹、建筑等元素等绘制都是通过这个函数来完成的,实现远离是利用来雪碧图,通过坐标抓取特定位置的图片元素来获取各种不同坦克等元素的UI;

    通过 rotate 旋转元素来实现坦克的转向;

    this.drawImageSlice = function drawImage(img_ele, sx, sy, sWidth, sHeight, x, y, rotatation) {  ctx.save()  ctx.translate((2 * x + sWidth) / 2, (2 * y + sHeight) / 2) // 改变起始点坐标  ctx.rotate((Math.PI / 180) * rotatation) // 旋转  x = x || 0  y = y || 0  ctx.drawImage(img_ele, sx, sy, sWidth, sHeight, -sWidth / 2, -sHeight / 2, sWidth, sHeight)  ctx.restore() // 复原}

    BattleCity 构造函数

    BattleCity 构造函数定义坦克的各种配置信息,和方法函数

    let TankConfig = function (cfg) {  this.explosion_count = cfg.explosion_count  this.width = cfg.type.dimension[0]  this.height = cfg.type.dimension[1]  this.missle_type = cfg.missle_type || MISSILE_TYPE.NORMAL  this.x = cfg.x || 0  this.y = cfg.y || 0  this.direction = cfg.direction || DIRECTION.UP  this.is_player = cfg.is_player || 0  this.moving = cfg.moving || 0  this.alive = cfg.alive || 1  this.border_x = cfg.border_x || 0  this.border_y = cfg.border_y || 0  this.speed = cfg.speed || TANK_SPEED  this.direction = cfg.direction || DIRECTION.UP  this.type = cfg.type || TANK_TYPE.PLAYER0}

    实现坦克的移动

    用键盘的 W、S、A、D、来表示上下左右方向键,按下键盘则会触发对应坦克实例的 move 函数,用于计算移动后的位置坐标信息,注意:对边界条件的判断,不可使其超出战场边界。

    CanvasSprite.prototype.move = function (d, obstacle_sprites) {    this.direction = d    switch (d) {      case DIRECTION.UP:        if ((obstacle_sprites && !this.checkRangeOverlap(obstacle_sprites)) || !obstacle_sprites) {          this.y -= this.speed          if (this.y <= 5) {            if (!this.out_of_border_die) {              this.y = 0            } else {              // this.alive = 0;              this.explode()              document.getElementById('steelhit').play()            }          }        }        break      case DIRECTION.DOWN:        if ((obstacle_sprites && !this.checkRangeOverlap(obstacle_sprites)) || !obstacle_sprites) {          this.y += this.speed          if (this.y + this.height >= this.border_y - 10) {            if (!this.out_of_border_die) {              this.y = this.border_y - this.height            } else {              // this.alive = 0;              this.explode()              document.getElementById('steelhit').play()            }          }        }        break      case DIRECTION.LEFT:        if ((obstacle_sprites && !this.checkRangeOverlap(obstacle_sprites)) || !obstacle_sprites) {          this.x -= this.speed          if (this.x <= 5) {            if (!this.out_of_border_die) {              this.x = 0            } else {              // this.alive = 0;              this.explode()              document.getElementById('steelhit').play()            }          }        }        break      case DIRECTION.RIGHT:        if ((obstacle_sprites && !this.checkRangeOverlap(obstacle_sprites)) || !obstacle_sprites) {          this.x += this.speed          if (this.x + this.width >= this.border_x - 10) {            if (!this.out_of_border_die) {              this.x = this.border_x - this.width            } else {              // this.alive = 0;              this.explode()              document.getElementById('steelhit').play()            }          }        }        break    }  }

    坦克发射子弹的逻辑

    首先需要定义子弹的配置信息以及构造函数;

    let MissileConfig = function (cfg) {  this.x = cfg.x  this.y = cfg.y  this.type = cfg.type || MISSILE_TYPE.NORMAL  this.width = cfg.width || this.type.dimension[0]  this.height = cfg.height || this.type.dimension[1]  this.direction = cfg.direction || DIRECTION.UP  this.is_from_player = cfg.is_from_player  this.out_of_border_die = cfg.out_of_border_die || 1 // 判断边界类型  this.border_x = cfg.border_x || 0  this.border_y = cfg.border_y || 0  this.speed = cfg.speed || TANK_SPEED  this.alive = cfg.alive || 1}    var Missile = function (MissileConfig) {      var x = MissileConfig.x      var y = MissileConfig.y      var width = MissileConfig.width      var height = MissileConfig.width      var direction = MissileConfig.direction      this.type = MissileConfig.type      this.is_from_player = MissileConfig.is_from_player || 0      var explosion_count = 0      CanvasSprite.apply(this, [        {          alive: 1,          out_of_border_die: 1,          border_y: HEIGHT,          border_x: WIDTH,          speed: MISSILE_SPEED,          direction: direction,          x: x,          y: y,          width: width,          height: height,        },      ])      this.isDestroied = function () {        return explosion_count > 0      }      this.explode = function () {        if (explosion_count++ === 5) {          this.alive = 0        }      }      this.getImg = function () {        if (explosion_count > 0) {          return {            width: TANK_EXPLOSION_FRAME[explosion_count].dimension[0],            height: TANK_EXPLOSION_FRAME[explosion_count].dimension[1],            offset_x: TANK_EXPLOSION_FRAME[explosion_count].image_coordinates[0],            offset_y: TANK_EXPLOSION_FRAME[explosion_count].image_coordinates[1],          }        } else {          return {            width: width,            height: height,            offset_x: this.type.image_coordinates[0],            offset_y: this.type.image_coordinates[1],          }        }      }      this.getHeadCoordinates = function () {        var h_x, h_y        switch (this.direction) {          case DIRECTION.UP:            h_x = this.x + this.width / 2 - this.type.dimension[0] / 2            h_y = this.y - this.type.dimension[1] / 2            break          case DIRECTION.DOWN:            h_x = this.x + this.width / 2 - this.type.dimension[0] / 2            h_y = this.y + this.height - this.type.dimension[1] / 2            break          case DIRECTION.LEFT:            h_x = this.x            h_y = this.y + this.width / 2 - this.type.dimension[0] / 2            break          case DIRECTION.RIGHT:            h_x = this.x + this.height            h_y = this.y + this.width / 2 - this.type.dimension[0] / 2        }        console.log({          x: h_x,          y: h_y,        })        return {          x: h_x,          y: h_y,        }      }      this._generateId = function () {        return uuidv4()      }      sprites[this._generateId()] = this    }

    然后再定义一个 fire 开发函数,当开火后,会使用 window.requestAnimationFrame() 来达到循环的效果,每次重绘最新的位置信息

    this.fire = function (boolean_type) {    if (!this.missle || !this.missle.alive) {      var coor = this.getCannonCoordinates()      this.missle = new Missile(        new MissileConfig({          x: coor.x,          y: coor.y,          direction: this.direction,          type: this.miss_type,          is_from_player: boolean_type,        })      )      if (boolean_type) {        document.getElementById('shoot').play()      }    }  }

    关于"怎么用Vue3+Canvas实现坦克大战游戏"这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对"怎么用Vue3+Canvas实现坦克大战游戏"知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。

    函数 坦克 画布 元素 坐标 位置 大战 信息 内容 子弹 知识 边界 不同 动态 图片 尺寸 文本 方法 核心 篇文章 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网络科技 麻工互联网科技 互联网与新科技 深圳齐家互联网科技 奔汉互联网科技 长亮科技 互联网金融 互联网科技创新企业排名 衡水科技产业互联网平台 太原新华互联网科技学校怎么样 老年人脱离互联网科技的作文 广州慧亚互联网科技怎么样 新兴科技互联网创新大赛颁奖典礼 乌鲁木齐新华智原互联网科技学校 内蒙古九一互联网科技 东莞新华互联网科技学校 下一代互联网重大应用科技园 互联网科技手机排行榜 张家口科技产业互联网平台 平安好医生打造互联网科技医疗 瑞安市蟠桃会互联网科技有限公司 今日互联网科技 没上市的科技互联网公司 互联网的科技创新 互联网科技热点资讯 四川慕妮娅互联网科技有限公司 滨湖区互联网智能科技产品有哪些 黑云互联网科技有限公司 美国互联网科技公司 互联网科技机房 六子台科技互联网展会
    0