iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >怎么用Vue3+Canvas实现坦克大战游戏
  • 567
分享到

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

2023-06-29 11:06:53 567人浏览 泡泡鱼
摘要

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

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

    架构搭建

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

    $ pnpm create vite <project-name> -- --template vue$ cd <project-name>$ 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)}

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

    画布重绘前的 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() // 复原}

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

    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    }  }

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

    坦克发射子弹的逻辑

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

    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实现坦克大战游戏”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“怎么用Vue3+Canvas实现坦克大战游戏”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

    --结束END--

    本文标题: 怎么用Vue3+Canvas实现坦克大战游戏

    本文链接: https://www.lsjlt.com/news/324464.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

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

    下载Word文档
    猜你喜欢
    • MySQL怎么更新字段长度
      要更新MySQL表中的字段长度,可以使用ALTER TABLE语句。以下是更新字段长度的示例: ALTER TABLE table_...
      99+
      2024-05-14
      mysql
    • sql如何修改表字段长度
      要修改表字段的长度,可以使用ALTER TABLE语句,然后使用MODIFY COLUMN来修改字段的长度。以下是一个示例: ALT...
      99+
      2024-05-14
      sql
    • mysql数据量太大如何解决
      有几种方法可以解决MySQL数据量过大的问题: 数据库分区:将数据库表按照某种规则进行分区,分成多个小表,可以提高查询速度和管理...
      99+
      2024-05-14
      mysql
    • MySQL单表数据量过大如何解决
      在MySQL中,当单表的数据量过大时,可以采取以下几种方法来解决问题: 使用索引:在表的字段上创建适当的索引,可以提高查询效率,...
      99+
      2024-05-14
      mysql
    • mysql导出数据量过大如何解决
      当导出的数据量过大时,可以尝试以下方法来解决问题: 使用数据库工具:使用数据库管理工具如Navicat或MySQL Workbe...
      99+
      2024-05-14
      mysql
    • mysql中log_bin的作用是什么
      在MySQL中,log_bin是二进制日志文件的名称,用于记录所有对数据库进行更改的操作,如插入、更新、删除等。这些二进制日志文件可...
      99+
      2024-05-14
      mysql
    • mysql中log_bin的特点有哪些
      log_bin是MySQL的二进制日志文件,用于记录所有对数据库的修改操作,包括插入、更新、删除等。 log_bin文件是M...
      99+
      2024-05-14
      mysql
    • mysql rpm包安装的方法是什么
      要在Linux系统上安装MySQL RPM包,可以按照以下步骤操作: 下载MySQL的RPM包:你可以从MySQL官方网站下载适...
      99+
      2024-05-14
      mysql
    • mysql rpm包安装后怎么使用
      安装MySQL RPM包后,您可以通过以下步骤来使用MySQL: 启动MySQL服务:使用以下命令来启动MySQL服务: sud...
      99+
      2024-05-14
      mysql
    • lxml中怎么处理XML命名空间默认值
      在lxml中处理XML命名空间的默认值可以通过使用xpath()方法和register_namespace()方法来实现。...
      99+
      2024-05-14
      lxml
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作