扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
利用vue3怎么重构一个拼图游戏?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
创新互联建站是一家专业从事网站制作、成都网站设计、网页设计的品牌网络公司。如今是成都地区具影响力的网站设计公司,作为专业的成都网站建设公司,创新互联建站依托强大的技术实力、以及多年的网站运营经验,为您提供专业的成都网站建设、营销型网站建设及网站设计开发服务!源代码臃肿,暴露的配置项不足,特备是和项目现有逻辑结合时体现的更加明显
生成的游戏可能出现无解情况,为了避免无解,只好写死几种情况然后随机生成
源代码是vue2版本,不支持vue3
最后决定使用 vue3 重新实现拼图游戏,着重注意以下细节
组件使用起来足够简单
可以自定义游戏难度
支持图片和数组两种模式
无论是拼图片还是拼数字,其原理都是要把原本打乱的数组移动成有序状态。网上也有很多实现数字华容的的算法,算法主要需要解决的就是如何生成一组 随机且有解 的数组,有的人可能有疑问,数组华容道还有可能无解吗?
如果生成的游戏像上面这样,那就是无解了。原理就像我们玩魔方一样,正常情况下不管我们打乱的多乱都可以还原,但是如果我们把 某几个块抠出来换换位置 ,那就可能还原不了了
网上也有很多算法可以避免生成无解的情况,但是写的都比较高深,加上自己阅读算法的能力有限,最后决定按照自己的思路实现。
我的思路其实比较简单,一句话总结就是逆向推理法,我们可以先从排列好的数组开始,然后随机的通过 移动 打乱其顺序,这样肯定可以保证生成的题目有解,同时可以根据打乱的步数来控制游戏的难度,真是个一举两得的idear。
数据存放
首先我考虑的是使用一个一维数组来存放数据
let arr = [1,2,3,4,5,6,7,8,0] // 0 代表空白
但是这样会出现一个问题,就是移动的时候逻辑变的相当麻烦,因为一维数组只能记录数据并不能记录竖直方向的位置,这个时候我们自然就想到使用二维数组了,比如,当我们需要生成一个3*3的游戏时数据是这样的:
let arr [ [1,2,3], [4,5,6], [7,8,0] ]
这样我们就可以通过下面来模拟x,y轴来表示每个数字的位置。比如0的位置是(2,2),6的位置是(1,2),如果我想移动6和0的位置,只需要把他们的坐标做调换即可。
移动函数
数字华容道最关键的交互就是用户点击那个块就移动哪个块,但是需要注意的是只有0附近的数组可以移动。下面我们先完成移动函数
function move(x, y, moveX, moveY) { const num = state.arr[x][y]; state.arr[x][y] = state.arr[moveX][moveY]; state.arr[moveX][moveY] = num; }
是不是很简单,其实就是把要移动的两个数的下标给交换下位置
有了移动函数,我们就可以实现上,下,左,右的移动了
// 上移动 function moveTop(x, y) { if (x <= 0) return -1; // 开始交换位置 const okx = x - 1; move(x, y, okx, y); return { x: okx, y, }; } //下移动 function moveDown(x, y) { if (x >= level - 1) return -1; const okx = x + 1; move(x, y, okx, y); return { x: okx, y, }; } // 左移动 function moveLeft(x, y) { if (y <= 0) return -1; const oky = y - 1; move(x, y, x, oky); return { x, y: oky, }; } // 右移动 function moveRight(x, y) { if (y >= level - 1) return -1; const oky = y + 1; move(x, y, x, oky); return { x, y: oky, }; }
现在我们再实现一个判断移动方向的方法,如下所示:
function shouldMove(x, y) { // 判断向哪移动 const { emptyX, emptyY } = seekEmpty(); if (x === emptyX && y !== emptyY && Math.abs(y - emptyY) === 1) { // 说明在一个水平线上 可能是左右移动 if (y > emptyY) { moveLeft(x, y); } else { moveRight(x, y); } } if (y === emptyY && x !== emptyX && Math.abs(x - emptyX) === 1) { // 说明需要上下移动 if (x > emptyX) { moveTop(x, y); } else { moveDown(x, y); } } }
if里面判断的意思是如果我们点击的块是空白快或者不是和空白快挨着的那个,那我们就不做任何处理
生成游戏面板
其实就是随机调用上移,下移,左移,右移函数,把数组打乱
// 随机打乱 function moveInit(diffic) { state.arr = creatArr(level); const num = diffic ? diffic : state.diffec; const fns = [moveTop, moveDown, moveLeft, moveRight]; let Index = null; let fn; for (let i = 0; i < num; i++) { Index = Math.floor(Math.random() * fns.length); // moveConsole(Index); fn = fns[Index](startX, startY); if (fn != -1) { const { x, y } = fn; startX = x; startY = y; } } }
短短几个函数,就完成了核心逻辑,还有几个函数没有介绍到比如判断游戏完成,寻找空白块的位置,创建二维数组大家可自行阅读源码
使用vue3重构
以上逻辑貌似和vue3没什么关系,但是我们忽略了最重要的一点,就是 改变数组后,视图也就改变了 这不就是响应式吗,使用vue3后我们可以把关于游戏的所有逻辑放到一个js里面,大大减少了代码的耦合度
const { arr, shouldMove, moveInit } = useCreateGame( gamedata.level, gamedata.difficulty, gameEndCallback );
可能有的人会有疑问?把所有逻辑抽离出来这不是很正常的操作吗?难道用vue2的时候都不能抽离了?
但是大家不要忘记了,我们的这个数组需要是响应式的,如果我们单独把逻辑抽离出来那我们在js文件里面改变数组还是响应式的吗
但当我们使用vue3的composition-api时就可以再js文件中声明一个响应式变量,且在组件中使用时它还是响应式的
export default function useCreateGame() { //声明一个响应式变量 ... const state = reactive({ arr: [], }); ... return { ...toRefs(state) ... } }
const { arr, shouldMove, moveInit } = useCreateGame( gamedata.level, gamedata.difficulty, gameEndCallback ); // 这个时候 arr 还是响应式的
这正是composition-api强大所在,有了composition-api我们可以任意组装我们的逻辑代码了
在vue2 如果要维护一个响应式变量我们是不是就要使用vuex这种状态管理器了,这样就增加了代码的耦合度
现在vite已经到了vite2版本,并且官方还在飞快迭代中,使用vite2创建的项目默认是可以使用setup新特性的,比如我们可以这样写:
{{ name }}
等价于这样写
{{ name }}
看完上述内容,你们掌握利用vue3怎么重构一个拼图游戏的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流