iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue3+Canvas实现坦克大战游戏(一)
  • 771
分享到

Vue3+Canvas实现坦克大战游戏(一)

2024-04-02 19:04:59 771人浏览 泡泡鱼
摘要

目录前言架构搭建canvas 构造函数画布绘制文本渲染画布重绘前的 clear核心:绘制函数BattleCity 构造函数实现坦克的移动坦克发射子弹的逻辑总结前言 记得几年前刚做前端

前言

记得几年前刚做前端开发的时候,跟着师傅用纯 es5 实现了这款坦克大战,可以说我入行前端是从 javascript 小游戏开始的,时间已匆匆过去了数年,前端发展日新月异,各种新框架、新概念层出不穷,很容易就迷失在对各种新技术的盲目学习和应用中,真正的编程是什么呢?值得思考的问题。

我准备用 vue3 重新实现一下这款游戏,顺便回顾和梳理下自己的知识体系。

W/上 S/下 A/左 D/右 F/射击

让我们开始吧!

架构搭建

项目技术选型为 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)
}

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

总结

利用 requestAnimationFrame 来实现循环刷新画布,通过修改各元素位置坐标值,在下一次画布重绘时更新视图,这是阶段交互的基本逻辑;

到这里已经实现了坦克移动和发射子弹的效果。

以上就是Vue3+Canvas实现坦克大战游戏(一)的详细内容,更多关于Vue3 Canvas坦克大战的资料请关注编程网其它相关文章!

--结束END--

本文标题: Vue3+Canvas实现坦克大战游戏(一)

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

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

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

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

下载Word文档
猜你喜欢
  • Vue3+Canvas实现坦克大战游戏(一)
    目录前言架构搭建Canvas 构造函数画布绘制文本渲染画布重绘前的 clear核心:绘制函数BattleCity 构造函数实现坦克的移动坦克发射子弹的逻辑总结前言 记得几年前刚做前端...
    99+
    2022-11-13
  • Vue3+Canvas实现坦克大战游戏(二)
    目录前言敌方坦克的生成坦克移动的算法子弹击中物体的算法爆炸效果的实现生成障碍物(石墙、砖墙等)总结前言 接着上篇讲,本篇主要给大家讲解一下子弹击中物体、物体销毁、敌方坦克构建生成、运...
    99+
    2022-11-13
  • 怎么用Vue3+Canvas实现坦克大战游戏
    这篇文章主要介绍了怎么用Vue3+Canvas实现坦克大战游戏的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Vue3+Canvas实现坦克大战游戏文章都会有所收获,下面我们一起来看看吧。架构搭建项目技术选...
    99+
    2023-06-29
  • Java Swing实现坦克大战游戏
    目录一、引言二、效果图三、实现四、完成一、引言 90坦克大战,很经典的一款游戏,当年与小伙伴一人一个手柄,搬上小板凳坐在电视机前,身体时不时跟随手柄摇晃着,时而表情严肃、眉头紧锁,时...
    99+
    2022-11-12
  • 如何实现Pygame坦克大战游戏
    这篇文章将为大家详细讲解有关如何实现Pygame坦克大战游戏,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。正文1)游戏规则:游戏过程是这样的,玩家操作坦克消灭电脑控制的坦克,并保护自己基地。基地图标是一只...
    99+
    2023-06-29
  • python实现双人版坦克大战游戏
    游戏介绍: 双人版的《坦克大战》的基本规则是玩家消灭出现的敌方坦克保卫我方基地。 中间还会随机出现很多特殊道具吸收可获得相应的功能,消灭玩即可进入下一关。 方向键:上下左右移动即可。...
    99+
    2022-11-12
  • Java编写实现坦克大战小游戏
    本文实例为大家分享了Java实现坦克大战小游戏的具体代码,供大家参考,具体内容如下 创作背景:n年前的学期末课题设计,从b站上学的,一个代码一个代码敲出来的。 小游戏介绍: 红色坦克...
    99+
    2022-11-12
  • JavaSwing坦克大战游戏的设计和实现
    目录需求分析:功能设计:具体设计:图形用户界面界面中的元素游戏截图:        还记得传说中的经典90坦克...
    99+
    2022-11-12
  • 怎么用JAVA实现经典游戏坦克大战
    这篇文章主要介绍“怎么用JAVA实现经典游戏坦克大战”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么用JAVA实现经典游戏坦克大战”文章能帮助大家解决问题。主要设计要有难度关卡:第一关,第二关,第...
    99+
    2023-06-29
  • python如何实现双人版坦克大战游戏
    本篇内容主要讲解“python如何实现双人版坦克大战游戏”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“python如何实现双人版坦克大战游戏”吧!游戏介绍:双人版的《坦克大战》的基本规则是玩家消...
    99+
    2023-06-22
  • 怎么用C语言实现游戏坦克大战
    本篇内容主要讲解“怎么用C语言实现游戏坦克大战”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用C语言实现游戏坦克大战”吧!首先就是我们载入图片的函数tupian.cpp# incl...
    99+
    2023-06-25
  • Pygame坦克大战游戏开发实战详解代码
    导语 哈喽!哈喽——我是木木子 今天来升级下之前写的坦克大战游戏嘛,哈哈哈 其实也不算是修改,就是稍微的调试一下!​​ 因为之前写的界面都是英文的 ,有的...
    99+
    2022-11-13
  • 使用Java怎么制作一个坦克大战游戏
    这篇文章给大家介绍使用Java怎么制作一个坦克大战游戏,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。package tankwar;import java.awt.Color;import ...
    99+
    2023-05-30
    java
  • JAVA实现经典游戏坦克大战的示例代码
    目录前言主要设计功能截图代码实现总结前言 小时候大家都玩过坦克大战吧,熟悉的旋律和丰富的关卡陪伴了我们一整个寒暑假,还记得传说中的经典坦克大战 吗?那些怀旧的记忆,伴随着我们一起走过...
    99+
    2022-11-13
  • Java实现坦克大战小游戏代码如何编写
    Java实现坦克大战小游戏代码如何编写,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。小游戏介绍:红色坦克是我们的操纵坦克,黑色是敌人坦克。上下左右键控制坦克移动...
    99+
    2023-06-26
  • C语言 完整游戏项目坦克大战详细代码
    话不多说 我们今天就来创造出属于我们自己的《坦克大战》,GOGOGO!!! 直接开始吧 这次的源码比较详细,我分了好几个cpp文件,思路更加的清晰,请耐心用心的观看 首先就是我们载...
    99+
    2022-11-12
  • HTML5如何实现经典坦克大战
    这篇文章主要为大家展示了“HTML5如何实现经典坦克大战”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“HTML5如何实现经典坦克大战”这篇文章吧。 ...
    99+
    2022-10-19
  • HTML5怎么实现经典坦克大战
    这篇文章主要介绍“HTML5怎么实现经典坦克大战”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“HTML5怎么实现经典坦克大战”文章能帮助大家解决问题。 代码如下:...
    99+
    2022-10-19
  • JavaScript实现飞机大战游戏
    本文实例为大家分享了canvas ,js 实现一个简单的飞机大战,供大家参考,具体内容如下 预览图: 代码: <!DOCTYPE html> <html>...
    99+
    2022-11-12
  • C++实现飞机大战游戏
    本文实例为大家分享了C++实现飞机大战游戏的具体代码,供大家参考,具体内容如下 代码是单线程执行,无界面,(博主下一步学习QT之后融入)还有待改进。先放张界面图: 话不多说 上...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作