溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用Vue3+Canvas實現坦克大戰(zhàn)游戲

發(fā)布時間:2022-03-11 09:08:38 來源:億速云 閱讀:262 作者:iii 欄目:開發(fā)技術

這篇文章主要介紹了怎么用Vue3+Canvas實現坦克大戰(zhàn)游戲的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇怎么用Vue3+Canvas實現坦克大戰(zhàn)游戲文章都會有所收獲,下面我們一起來看看吧。

    架構搭建

    項目技術選型為 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 圖層按照一般的游戲設計優(yōu)化理念,需要為靜態(tài)背景和動態(tài)元素單獨用不同的 canvas 圖層表示,每次更新時只需要重新繪制動態(tài)元素就好了,我抽象出一個渲染函數

    // 渲染
    this.renderTo = function renderTo(container_id) {
      if (!is_rendered) {
        let container = document.getElementById(container_id)
        //畫布起始坐標
        dom = document.createElement('canvas') // 創(chuàng)造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容器
      }
    }

    文本渲染

    想要知道畫布中的具體位置坐標,可以定義一個函數,當鼠標滑動時候執(zhí)行來將當前位置坐標繪制出來

    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實現坦克大戰(zhàn)游戲

    畫布重繪前的 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實現坦克大戰(zhàn)游戲

    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、來表示上下左右方向鍵,按下鍵盤則會觸發(fā)對應坦克實例的 move 函數,用于計算移動后的位置坐標信息,注意:對邊界條件的判斷,不可使其超出戰(zhàn)場邊界。

    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實現坦克大戰(zhàn)游戲

    坦克發(fā)射子彈的邏輯

    首先需要定義子彈的配置信息以及構造函數;

    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 開發(fā)函數,當開火后,會使用 window.requestAnimationFrame() 來達到循環(huán)的效果,每次重繪最新的位置信息

    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實現坦克大戰(zhàn)游戲

    關于“怎么用Vue3+Canvas實現坦克大戰(zhàn)游戲”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“怎么用Vue3+Canvas實現坦克大戰(zhàn)游戲”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI