基于Vue3制作简单的消消乐游戏

目录
  • 游戏介绍
  • 核心思路
  • 核心代码
    • html
    • js
    • 游戏开始/循环
    • 删除
    • 下落
    • 添加
    • 生成矩阵/数据
    • 点击
    • 换位置

游戏介绍

先看一下

好吧,我知道界面有点丑 →_→

核心思路

游戏步骤主要就是:消除、下落、补充、移动,采用三种状态来区分需要删除的(remove)、新添加的(add)、和正常的方块(normal)

  • 主要就是生成小方块列表后,马上保存每一个方块上下左右方块的信息
  • 然后判断每一个方块和上下或和左右类型相同即为需要消除,并把该方块状态改为 remove
  • 然后通过定位改变 top 来控制下落,同时要把消除的位置上移,这样补充的时候才能在对应空位上显示,这里专门用了一个矩阵来保存所有对应格子信息,区分出哪些格子是需要消除/补充的
  • 移动就比较简单了,由于每个方块上都保存了自己的上下左右信息,所以只需要交换就行了

有一个坑,就是 key,由于 diff 算法的原因,不需要重新渲染就要保证key是唯一的,比如下落的也重新渲染视觉效果会很奇怪

核心代码

html

以下是矩阵区域所有html,就是用一个div来做的,根据类型给不同类名,然后雪糕全是背景图片

<div class="stage">
  <div
    v-for="item in data"
    :style="{
      left: `${item.positionLeft}px`,
      top: `${item.positionTop}px`,
    }"
    :key="item.key"
    :class="[
      'square',
      `type${item.type}`,
      `scale${item.scale}`,
      { active: item.active },
    ]"
    @click="handleClick(item)"
  ></div>
</div>

js

js 部分主要是封装了一个类,方便统一管理操作

export default class Stage implements IXXL {
  x: number // x和y 是游戏舞台行列方块个数
  y: number
  size: number // 方块大小
  typeCount = 7 // 方块类型个数
  matrix: Array<any> = [] // 方块矩阵,用于每次消除之后根据矩阵规则生成新的游戏棋盘
  data: Array<any> = [] // 用于渲染页面
  isHandle = false // 游戏是否正在消除/下落/添加处理中
  isSelect = false // 是否有选择
  score = 0 // 分数
  target1: any = { active: false } // 选中的方块
  target2: any = {}
  constructor(x: number, y: number, size: number) {
    this.x = x
    this.y = y
    this.size = size
    this.getMatrix() // 生成矩阵
    this.init(true) // 生成 data 渲染用
  }
  getMatrix(){}
  init(){}
  // 循环执行
  gameLoop(){}
  // 点击
  click(){}
  // 换位
  swap(){}
  // 删除
  remove(){}
  // 下落
  down(){}
  // 补充
  add(){}
}

游戏开始/循环

// 要等动画执行完,所以用 await
async gameLoop(bool: boolean = false) {
    // 结束游戏后重新开始时分数清0
    if (bool) this.score = 0
    // 游戏状态改为正在执行中,控制在动画执行过程中不能点击交换
    this.isHandle = true
    // 找出需要删除的
    await this.remove()
    // 用于检测点击交换后判断有没有需要删除的,没有就再换回来
    let status = this.data.some((item) => item.status === "remove")
    // 只要有删除了的,执行上面的下落、补充,补充后再循环找有没有可以删除的
    while (this.data.some((item) => item.status === "remove")) {
      await this.down()
      await this.add()
      await this.remove()
    }
    // 所有能删除的删除后,更改状态,然后就可以点击了
    this.isHandle = false
    return status
}

删除

注意 状态为 remove 的实际没有删除,只是页面上看不到了,到补充的时候才会删除掉状态为 remove

// 清除
remove() {
    return new Promise((resolve, reject) => {
      const { data } = this
      data.forEach((item) => {
        const { left, right, top, bottom, type } = item
        // 如果自己 + 自己的左和右 类型都一样,状态变更为删除
        if (left?.type == type && right?.type == type) {
          left.status = "remove"
          item.status = "remove"
          right.status = "remove"
        }
        // 如果自己 + 自己的上和下 类型都一样,状态变更为删除
        if (top?.type == type && bottom?.type == type) {
          top.status = "remove"
          item.status = "remove"
          bottom.status = "remove"
        }
      })
      setTimeout(() => {
        // 执行删除动画,页面上看不到了,并统计分数,实际这时还没删除
        data.forEach((item, index) => {
          if (item.status === "remove") {
            item.scale = 0
            this.score += 1
          }
        })
        // 这里延迟100毫秒是首次进页面的时候,先看到格子有东西,不然会是空的
      }, 100)
      // 动画时长500毫秒 css 那边定义了,所以延迟500毫秒
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

下落

这里有个坑。除了要把删除格子上面的下落下来之外,还需要把已经删除(状态为删除,页面上看不到了的)的格子上位到,上面的空位上,否则,新增的格子会从下面冒出来

// 下落
down() {
    return new Promise((resolve, reject) => {
      const { data, size, x, y } = this
      data.forEach((item, index) => {
        let distance = 0 // 移动格数
        if (item.status === "remove") {
          // 删除的位置上移,调整新增格子的位置
          let top = item.top
          // 统计需要上移多少步
          while (top) {
            if (top.status !== "remove") {
              distance += 1
            }
            top = top.top
          }
          // 上移
          if (distance) {
            item.y -= distance
            item.positionTop = item.positionTop - size * distance
          }
        } else {
          let bottom = item.bottom
          // 统计需要下落多少步
          while (bottom) {
            if (bottom.status === "remove") {
              distance += 1
            }
            bottom = bottom.bottom
          }
          // 下落
          if (distance) {
            item.y += distance
            item.positionTop = item.positionTop + size * distance
          }
        }
      })
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

添加

可以想象到,在下落执行完之后,页面中的矩阵,是所有格子都有的,只是看起来空的格子,实际上是删除格子在那占位,然后只要根据顺序重新生成矩阵,并保留每个非remove格子的状态,是remove的就重新生成,达到替换补充的效果

// 添加
add() {
    return new Promise((resolve, reject) => {
      const { size, matrix } = this
      // 重置矩阵为空
      this.getMatrix()
      // 把当前所有格子信息保存为矩阵
      this.matrix = matrix.map((row, rowIndex) =>
        row.map((col: any, colIndex: number) => {
          return this.data.find((item) => {
            return colIndex == item.x && rowIndex == item.y
          })
        })
      )
      // 根据矩阵需要清除的位置替换新方块
      this.init()
      setTimeout(() => {
        // 新增的格子执行动画
        this.data.forEach((item) => {
          if (item.status === "add") {
            item.scale = 1
            item.status = "normal"
          }
        })
      }, 100)
      // 动画结束
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

接下来后面的逻辑都比较简单了,没啥说的,都写在注释里了

生成矩阵/数据

// 生成全部为空的矩阵
getMatrix() {
    const { x, y } = this
    const row = new Array(x).fill(undefined)
    const matrix = new Array(y).fill(undefined).map((item) => row)
    this.matrix = matrix
}
// 生成小方块
init(bool: boolean = false) {
    const { x, y, typeCount, matrix, size } = this
    const data: Array<any> = []
    // 这里用两个指针,没有用嵌套循环,减少复杂度
    let _x = 0
    let _y = 0
    for (let i = 0, len = Math.pow(x, 2); i < len; i++) {
      let item
      try {
        item = matrix[_y][_x]
      } catch (e) {}
      // 根据矩阵信息来生成方块
      let flag: boolean = item && item.status !== "remove"
      // 每一个方块的信息
      let obj = {
        type: flag ? item.type : Math.floor(Math.random() * typeCount),
        x: _x,
        y: _y,
        status: bool ? "normal" : flag ? "normal" : "add",
        positionLeft: flag ? item.positionLeft : size * _x,
        positionTop: flag ? item.positionTop : size * _y,
        left: undefined,
        top: undefined,
        bottom: undefined,
        right: undefined,
        scale: bool ? 1 : flag ? 1 : 0,
        key: item ? item.key + i : `${_x}${_y}`,
        active: false,
      }
      data.push(obj)
      _x++
      if (_x == x) {
        _x = 0
        _y++
      }
    }
    // 保存每个格子上下左右的格子信息
    data.forEach((square) => {
      square.left = data.find(
        (item) => item.x == square.x - 1 && item.y == square.y
      )
      square.right = data.find(
        (item) => item.x == square.x + 1 && item.y == square.y
      )
      square.top = data.find(
        (item) => item.x == square.x && item.y == square.y - 1
      )
      square.bottom = data.find(
        (item) => item.x == square.x && item.y == square.y + 1
      )
    })
    this.data = data
}

点击

// 点击小方块
click(target: any) {
    // 游戏动画正在处理中的时候,不给点击
    if (this.isHandle) return
    // console.log(target)
    const { isSelect } = this
    // 如果没有选择过的
    if (!isSelect) {
      // 选择第一个
      target.active = true
      this.target1 = target
      this.isSelect = true
    } else {
      // 选择第二个
      if (this.target1 === target) return
      this.target1.active = false
      // 如果是相邻的
      if (
        ["left", "top", "bottom", "right"].some(
          (item) => this.target1[item] == target
        )
      ) {
        this.target2 = target
        ;(async () => {
          // 调换位置
          await this.swap()
          // 会返回一个有没有可以删除的,的状态
          let res = await this.gameLoop()
          // 没有就再次调换位置,还原
          if (!res) {
            await this.swap()
          }
        })()
        this.isSelect = false
      } else {
        // 如果不是相邻的
        target.active = true
        this.target1 = target
        this.isSelect = true
      }
    }
}

换位置

这里的逻辑主要就是交换两个方块的位置信息,然后重新生成上下左右,就ok 了

// 换位置
swap() {
    return new Promise((resolve, reject) => {
      const { target1, target2, data } = this
      const { positionLeft: pl1, positionTop: pt1, x: x1, y: y1 } = target1
      const { positionLeft: pl2, positionTop: pt2, x: x2, y: y2 } = target2
      setTimeout(() => {
        target1.positionLeft = pl2
        target1.positionTop = pt2
        target1.x = x2
        target1.y = y2
        target2.positionLeft = pl1
        target2.positionTop = pt1
        target2.x = x1
        target2.y = y1
        data.forEach((square) => {
          square.left = data.find(
            (item) => item.x == square.x - 1 && item.y == square.y
          )
          square.right = data.find(
            (item) => item.x == square.x + 1 && item.y == square.y
          )
          square.top = data.find(
            (item) => item.x == square.x && item.y == square.y - 1
          )
          square.bottom = data.find(
            (item) => item.x == square.x && item.y == square.y + 1
          )
        })
      }, 0)
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

到此这篇关于基于Vue3制作简单的消消乐游戏的文章就介绍到这了,更多相关Vue3消消乐游戏内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue3+Canvas实现坦克大战游戏

    目录 前言 架构搭建 Canvas构造函数 画布绘制 文本渲染 画布重绘前的clear 核心:绘制函数 BattleCity构造函数 实现坦克的移动 坦克发射子弹的逻辑 总结 前言 记得几年前刚做前端开发的时候,跟着师傅用纯 es5 实现了这款坦克大战,可以说我入行前端是从 javaScript 小游戏开始的,时间已匆匆过去了数年,前端发展日新月异,各种新框架.新概念层出不穷,很容易就迷失在对各种新技术的盲目学习和应用中,真正的编程是什么呢?值得思考的问题. 我准备用 vue3 重新实现一下这款

  • 手把手教你用vue3开发一个打砖块小游戏

    前言 用vue3写了几个实例,感觉Vue3的composition Api设计得还是很不错,改变了一下习惯,但写多两个就好了. 这次写一个也是儿时很觉得很好玩的游戏-打砖块, 无聊的时候玩一下也觉得挺好玩,游戏性也挺高.这次我直接用vite+vue3打包尝试一下,vite也是开箱即用,特点是也是可以清除死代码,按需打包,所以打包速度也是非常快的.没用过的同学可以尝试用用. 游戏效果 游戏需求 创建一个场景 创建一个球,创建一堆被打击方块 创建一个可以移动方块并可控制左右移动 当球碰撞左右上边界及

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

    目录 前言 敌方坦克的生成 坦克移动的算法 子弹击中物体的算法 爆炸效果的实现 生成障碍物(石墙.砖墙等) 总结 前言 接着上篇讲,本篇主要给大家讲解一下子弹击中物体.物体销毁.敌方坦克构建生成.运动算法.爆炸效果.以及障碍物的生成:了解了这些我相信你可以不依赖游戏引擎实现大部分小游戏的开发. Es5版本: 在线游戏 源代码 W/上 S/下 A/左 D/右 F/射击 让我们开始吧! 敌方坦克的生成 我们可以使用 for 循环和Tank 构造函数,批量制造多个敌方坦克,x,y 为其在画布中的坐标,

  • 基于vite2+vue3制作个招财猫游戏

    目录 介绍 演示 正文 游戏分析 招财动画 素材加载 条带生成 无限滚动 中奖判定 纸屑飞舞 结语 介绍 端午将至,大家都开始吃粽子了么,你是喜欢吃北方的甜的红枣粽?还是南方的大肉粽呢? 本期我们将使用vite2与vue3开发出一个招财猫小游戏,通过考验眼力和预判能力,在图案不停滚动的同时选出可以转出不同的素材最终得到粽子奖励,康康你能用多少次才会转出自己喜爱口味的粽子吧~ 演示 预览地址:jsmask.gitee.io/dwgame_laohuji/ 正文 游戏分析 在开发之前,我们要想好游戏

  • Vue实现开心消消乐游戏算法

    之前做过一个算法题,算法要求就是写一个开心消消乐的逻辑算法,当时也是考虑了一段时间才做出来.后来想了想,既然核心算法都有了,能不能实现一个开心消消乐的小游戏呢,于是花了两天时间做了一个小游戏出来. 效果展示# 先在这里放一个最终实现的效果,还是一个比较初级的版本,大家有什么想法欢迎评论哦 游戏规则: 初始时会给玩家十分的初始分,每拖动一次就减一分,每消除一个方块就加一分,直到最后分数为0游戏结束 任意两个方块都可以拖动 界面设计# 页面的布局比较简单,格子的数据是一个二维数组的形式,说到这里大家

  • 基于Vue3实现数字华容道游戏的示例代码

    目录 前言 环境 思路 实现 GameCnt GameTool GamePass GameTip Menu 最后 前言 恰逢春之四月,天气忽热忽凉,遇游戏大赛,以笨拙之技,书一篇小文. 游戏规则:存在n*n的格子,需要将它们按数字顺序或图片顺序一一还原即可. 环境 主要环境: vue3 version:3.2.4 vite version:2.5.0 vue-router version:4.0.14 注:这个游戏的路由使用的是自动路由插件 主要插件: windicss version:3.5.

  • 基于Vue3制作简单的消消乐游戏

    目录 游戏介绍 核心思路 核心代码 html js 游戏开始/循环 删除 下落 添加 生成矩阵/数据 点击 换位置 游戏介绍 先看一下 好吧,我知道界面有点丑 →_→ 核心思路 游戏步骤主要就是:消除.下落.补充.移动,采用三种状态来区分需要删除的(remove).新添加的(add).和正常的方块(normal) 主要就是生成小方块列表后,马上保存每一个方块上下左右方块的信息 然后判断每一个方块和上下或和左右类型相同即为需要消除,并把该方块状态改为 remove 然后通过定位改变 top 来控制

  • 基于replaceChild制作简单的吞噬特效

    效果演示图: 源   码   查   看 [HTML代码说明] <ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class="in">4</

  • 基于Java制作一个好玩的打飞机游戏

    目录 1.效果图 2.项目整体构造 3.主类代码展示 4.飞机类代码展示 5.炮弹类代码展示 6.爆炸类代码展示 1.效果图 2.项目整体构造 3.主类代码展示 public class MyGameFrame  extends  Frame {          Image   planeImg  = GameUtil.getImage("images/plane.png");     Image   bg  = GameUtil.getImage("images/bg.j

  • 基于insertBefore制作简单的循环插空效果

    效果图展示: 源码查看 [功能说明] 利用insertBefore制作简单的循环插空效果 [HTML代码说明] <ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class=

  • 基于Python编写一个宝石消消乐小游戏

    目录 开发工具 环境搭建 原理简介 开发工具 python版本:3.6.4 相关模块: pygame:以及一些python自带的模块. 环境搭建 安装python并添加到环境变量,pip安装需要的相关模块即可. 原理简介 游戏规则: 玩家通过鼠标交换相邻的拼图,若交换后水平/竖直方向存在连续三个相同的拼图,则这些拼图消失,玩家得分,同时生成新的拼图以补充消失的部分,否则,交换失败,玩家不得分.玩家需要在规定时间内获取尽可能高的得分. 实现过程: 首先加载一些必要的游戏素材: 加载背景音乐: py

  • 基于Python实现开心消消乐小游戏的示例代码

    目录 前言 一.准备 1.1 图片素材 1.2 音频素材 二.代码 2.1 导入模块 2.2 游戏音乐设置 2.3 制作树类 2.4 制作鼠标点击效果 2.5 制作出现元素 2.6 数组 2.7 制作人物画板 三.效果展示(仅部分) 3.1 初始页面 3.2 第一关画面 3.3 失败画面 3.4 第十关画面 穿过云朵升一级是要花6个金币的,有的时候金币真的很重要 前言 嗨喽,大家好呀!这里是魔王~ 一天晚上,天空中掉下一颗神奇的豌豆种子,正好落在了梦之森林的村长屋附近. 种子落地后吸收了池塘的水

  • 基于JS实现的消消乐游戏的示例代码

    目录 前言 游戏的准备工作 总结一下 棋盘 渲染画面 动画效果 genCollapse() genDownfall() genEmerge() 整合效果 genLoop() genSwap() 前言 一直对小游戏挺感兴趣的,也尝试着做过一些小游戏,实现游戏是一个不错的提高代码基础水平的方式,因此这次挑战了一个较为困难的小游戏:消消乐. 如果仅仅是从消除方面来制作一个静态的消消乐(只有消除和补充方块的过程,但是没有任何交互和动画)其实并不算太难,只要我们能够想通方块消除(主要是三消)的原理和方块下

  • 基于JS编写开心消消乐游戏的示例代码

    目录 展示 游戏背景 游戏规则 源码部分 对关卡的地图设置 介绍一下游戏中的一些功能 展示 游戏背景 一天晚上,天空中掉下一颗神奇的豌豆种子,正好落在了梦之森林的村长屋附近,种子落地后吸收了池塘的水分,迅速成长,一夜之间变成参天大藤蔓…… 第二天早上,村民们醒来后看到巨大的藤蔓都惊呆了,聚在一起议论纷纷.有人说他似乎看到村长的房子在高耸入云的藤蔓上,房子似乎还在上升,有人号召说应该爬上去救村长,玩家需要爬到藤曼顶部救出村长 游戏规则 把三个颜色相同的小动物连成一条直线,即可消除.达到指定的目标通

  • 使用python+pygame开发消消乐游戏附完整源码

    效果是这样的 ↓ ↓ ↓ 一.环境要求 windows系统,python3.6+ pip21+ 开发环境搭建地址 一起来学pygame吧 游戏开发30例(开篇词)--环境搭建+游戏效果展示 安装游戏依赖模块 pip install pygame 二.游戏简介 消消乐应该大家都玩过,或者看过.这个花里胡哨的小游戏 用python的pygame来实现,很简单. 今天带大家,用Python来实现一下这个花里胡哨的小游戏. 三.完整开发流程 1.项目主结构 首先,先整理一下项目的主结构,其实看一下主结构

随机推荐