利用Vue.js实现拼图游戏

2023-12-19 00:58

本文主要是介绍利用Vue.js实现拼图游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前写过一篇《基于Vue.js的表格分页组件》的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/luozhihao/p/5516065.html

前言

为了进一步让大家了解Vue.js的神奇魅力,了解Vue.js的一种以数据为驱动的理念,本文主要利用Vue实现了一个数字拼图游戏,其原理并不是很复杂,效果图如下:

demo展示地址为:https://luozhihao.github.io/vue-puzzle/index.html#!/

有能力的可以玩玩,拼出来有赏哦~~

功能分析

当然玩归玩,作为一名Vue爱好者,我们理应深入游戏内部,一探代码的实现。接下来我们就先来分析一下要完成这样的一个游戏,主要需要实现哪些功能。下面我就直接将此实例的功能点罗列在下了:

  • 随机生成1~15的数字格子,每一个数字都必须出现且仅出现一次

  • 点击一个数字方块后,如其上下左右有一处为空,则两者交换位置

  • 格子每移动一步,我们都需要校验其是否闯关成功

  • 点击重置游戏按钮后需对拼图进行重新排序

以上便是本实例的主要功能点,可见游戏功能并不复杂,我们只需一个个攻破就OK了,接下来我就来展示一下各个功能点的Vue代码。

构建游戏面板

作为一款以数据驱动的JS框架,Vue的HTML模板很多时候都应该绑定数据的,比如此游戏的方块格子,我们这里肯定是不能写死的,代码如下:

复制代码
<template><div class="box"><ul class="puzzle-wrap"><li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle"></li></ul></div>
</template><script>
export default {data () {return {puzzles: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]}}
}
</script>
复制代码

这里我省略了css样式部分,大家可以先不用关心。以上代码我们将1~15的数字写死在了一个数组中,这显然不是随机排序的,那么我们就来实现随机排序的功能。

随机排序数字

复制代码
<template><div class="box"><ul class="puzzle-wrap"><li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle"></li></ul></div>
</template><script>
export default {data () {return {puzzles: []}},methods: {// 重置渲染
        render () {let puzzleArr = [],i = 1// 生成包含1 ~ 15数字的数组for (i; i < 16; i++) {puzzleArr.push(i)}// 随机打乱数组puzzleArr = puzzleArr.sort(() => {return Math.random() - 0.5});// 页面显示this.puzzles = puzzleArrthis.puzzles.push('')},},ready () {this.render()}
}
复制代码

以上代码,我们利用for循环生成了一个1~15的有序数组,之后我们又利用原生JS的sort方法随机打乱数字,这里还包含了一个知识点就是Math.random()方法。
利用sort()方法进行自定义排序,我们需要提供一个比较函数,然后返回一个用于说明这两个值的相对顺序的数字,其返回值如下:

  • 返回一个小于 0 的值,说明 a 小于 b

  • 返回 0,说明 a 等于 b

  • 返回一个大于 0 的值,说明 a 大于 b

这里利用Math.random()生成一个 0 ~ 1 之间的随机数,再减去0.5,这样就会有一半概率返回一个小于 0 的值, 一半概率返回一个大于 0 的值,就保证了生成数组的随机性,实现了动态随机生成数字格子的功能。

需要注意的是,我们还在数组最后插了一个空字符串,用来生成唯一的空白格子。

交换方块位置

复制代码
<template><div class="box"><ul class="puzzle-wrap"><li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle"@click="moveFn($index)"></li></ul></div>
</template><script>
export default {data () {return {puzzles: []}},methods: {// 重置渲染
        render () {let puzzleArr = [],i = 1// 生成包含1 ~ 15数字的数组for (i; i < 16; i++) {puzzleArr.push(i)}// 随机打乱数组puzzleArr = puzzleArr.sort(() => {return Math.random() - 0.5});// 页面显示this.puzzles = puzzleArrthis.puzzles.push('')},// 点击方块
        moveFn (index) {// 获取点击位置及其上下左右的值let curNum = this.puzzles[index],leftNum = this.puzzles[index - 1],rightNum = this.puzzles[index + 1],topNum = this.puzzles[index - 4],bottomNum = this.puzzles[index + 4]// 和为空的位置交换数值if (leftNum === '' && index % 4) {this.puzzles.$set(index - 1, curNum)this.puzzles.$set(index, '')} else if (rightNum === '' && 3 !== index % 4) {this.puzzles.$set(index + 1, curNum)this.puzzles.$set(index, '')} else if (topNum === '') {this.puzzles.$set(index - 4, curNum)this.puzzles.$set(index, '')} else if (bottomNum === '') {this.puzzles.$set(index + 4, curNum)this.puzzles.$set(index, '')}}},ready () {this.render()}
}
</script>
复制代码
  1. 这里我们首先在每个格子的li上添加了点击事件@click="moveFn($index)",通过$index参数获取点击方块在数组中的位置

  2. 其次获取其上下左右的数字在数组中的index值依次为index - 4、index + 4、index - 1、index + 1

  3. 当我们找到上下左右有一处为空的时候我们将空的位置赋值上当前点击格子的数字,将当前点击的位置置为空

备注:我们为什么要使用$set方法,而不直接用等号赋值呢,这里包含了Vue响应式原理的知识点。

复制代码
// 因为 JavaScript 的限制,Vue.js 不能检测到下面数组变化:// 1.直接用索引设置元素,如 vm.items[0] = {};
// 2.修改数据的长度,如 vm.items.length = 0。
// 为了解决问题 (1),Vue.js 扩展了观察数组,为它添加了一个 $set() 方法:// 与 `example1.items[0] = ...` 相同,但是能触发视图更新
example1.items.$set(0, { childMsg: 'Changed!'})
复制代码

详见:http://cn.vuejs.org/guide/list.html#问题

检测是否闯关成功

复制代码
<template><div class="box"><ul class="puzzle-wrap"><li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle"@click="moveFn($index)"></li></ul></div>
</template><script>
export default {data () {return {puzzles: []}},methods: {// 重置渲染
        render () {let puzzleArr = [],i = 1// 生成包含1 ~ 15数字的数组for (i; i < 16; i++) {puzzleArr.push(i)}// 随机打乱数组puzzleArr = puzzleArr.sort(() => {return Math.random() - 0.5});// 页面显示this.puzzles = puzzleArrthis.puzzles.push('')},// 点击方块
        moveFn (index) {// 获取点击位置及其上下左右的值let curNum = this.puzzles[index],leftNum = this.puzzles[index - 1],rightNum = this.puzzles[index + 1],topNum = this.puzzles[index - 4],bottomNum = this.puzzles[index + 4]// 和为空的位置交换数值if (leftNum === '' && index % 4) {this.puzzles.$set(index - 1, curNum)this.puzzles.$set(index, '')} else if (rightNum === '' && 3 !== index % 4) {this.puzzles.$set(index + 1, curNum)this.puzzles.$set(index, '')} else if (topNum === '') {this.puzzles.$set(index - 4, curNum)this.puzzles.$set(index, '')} else if (bottomNum === '') {this.puzzles.$set(index + 4, curNum)this.puzzles.$set(index, '')}this.passFn()},// 校验是否过关
        passFn () {if (this.puzzles[15] === '') {const newPuzzles = this.puzzles.slice(0, 15)const isPass = newPuzzles.every((e, i) => e === i + 1)if (isPass) {alert ('恭喜,闯关成功!')}}}},ready () {this.render()}
}
</script>
复制代码

我们在moveFn方法里调用了passFn方法来进行检测,而passFn方法里又涉及了两个知识点:
(1)slice方法

通过slice方法我们截取数组的前15个元素生成一个新的数组,当然前提是数组随后一个元素为空

(2)every方法

通过every方法我们来循环截取后数组的每一个元素是否等于其index+1值,如果全部等于则返回true,只要有一个不等于则返回false

如果闯关成功那么isPass的值为true,就会alert "恭喜,闯关成功!"提示窗,如果没有则不提示。

重置游戏

重置游戏其实很简单,只需添加重置按钮并在其上调用render方法就行了:

复制代码
<template><div class="box"><ul class="puzzle-wrap"><li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle"@click="moveFn($index)"></li></ul><button class="btn btn-warning btn-block btn-reset" @click="render">重置游戏</button></div>
</template><script>
export default {data () {return {puzzles: []}},methods: {// 重置渲染
        render () {let puzzleArr = [],i = 1// 生成包含1 ~ 15数字的数组for (i; i < 16; i++) {puzzleArr.push(i)}// 随机打乱数组puzzleArr = puzzleArr.sort(() => {return Math.random() - 0.5});// 页面显示this.puzzles = puzzleArrthis.puzzles.push('')},// 点击方块
        moveFn (index) {// 获取点击位置及其上下左右的值let curNum = this.puzzles[index],leftNum = this.puzzles[index - 1],rightNum = this.puzzles[index + 1],topNum = this.puzzles[index - 4],bottomNum = this.puzzles[index + 4]// 和为空的位置交换数值if (leftNum === '' && index % 4) {this.puzzles.$set(index - 1, curNum)this.puzzles.$set(index, '')} else if (rightNum === '' && 3 !== index % 4) {this.puzzles.$set(index + 1, curNum)this.puzzles.$set(index, '')} else if (topNum === '') {this.puzzles.$set(index - 4, curNum)this.puzzles.$set(index, '')} else if (bottomNum === '') {this.puzzles.$set(index + 4, curNum)this.puzzles.$set(index, '')}this.passFn()},// 校验是否过关
        passFn () {if (this.puzzles[15] === '') {const newPuzzles = this.puzzles.slice(0, 15)const isPass = newPuzzles.every((e, i) => e === i + 1)if (isPass) {alert ('恭喜,闯关成功!')}}}},ready () {this.render()}
}
</script><style>
@import url('./assets/css/bootstrap.min.css');body {font-family: Arial, "Microsoft YaHei"; 
}.box {width: 400px;margin: 50px auto 0;
}.puzzle-wrap {width: 400px;height: 400px;margin-bottom: 40px;padding: 0;background: #ccc;list-style: none;
}.puzzle {float: left;width: 100px;height: 100px;font-size: 20px;background: #f90;text-align: center;line-height: 100px;border: 1px solid #ccc;box-shadow: 1px 1px 4px;text-shadow: 1px 1px 1px #B9B4B4;cursor: pointer;
}.puzzle-empty {background: #ccc;box-shadow: inset 2px 2px 18px;
}.btn-reset {box-shadow: inset 2px 2px 18px;
}
</style>
复制代码

这里我一并加上了css代码。

总结

其实本游戏的代码量不多,功能点也不是很复杂,不过通过Vue来写这样的游戏,有助于我们了解Vue以数据驱动的响应式原理,在简化代码量的同时也增加了代码的可读性。

本实例的所有源码我已经上传至我的github,地址为https://github.com/luozhihao/vue-puzzle 需要的童鞋可以自行下载运行。

 

原创文章,转载请注明来自一个萝卜一个坑 -博客园[http://www.cnblogs.com/luozhihao] 

本文地址:http://www.cnblogs.com/luozhihao/p/5726661.html

本文同步发表于:https://segmentfault.com/a/1190000006137236

这篇关于利用Vue.js实现拼图游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/510382

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo