• 售前

  • 售后

热门帖子
入门百科

使用vue3重构拼图游戏的实现示例

[复制链接]
淡情惜缘缘wt 显示全部楼层 发表于 2021-10-25 20:28:03 |阅读模式 打印 上一主题 下一主题
前言


花了两天时间,重构了项目中的一个拼图小游戏(又名数字华容道),为了方便利用抽离成了独立组件,效果如下:


线上体验

源码地点在文章最后哦!

重要重构点


原有拼图游戏是通过开源代码加以改造,利用的是 vue2 。在现实项目利用一切正常,但还是存在以下痛点
      
  • 源代码臃肿,袒露的设置项不敷,特备是和项目现有逻辑团结时体现的更加明显  
  • 天生的游戏大概出现无解情况,为了避免无解,只好写死几种情况然后随机天生  
  • 源代码是vue2版本,不支持vue3
最后决定利用 vue3 重新实现拼图游戏,着重注意以下细节
      
  • 组件利用起来足够简单  
  • 可以自界说游戏难度  
  • 支持图片和数组两种模式
实现思路


无论是拼图片还是拼数字,其原理都是要把本来打乱的数组移动成有序状态。网上也有许多实现数字华容的的算法,算法重要必要解决的就是怎样天生一组 随机且有解 的数组,有的人大概有疑问,数组华容道还有大概无解吗?

如果天生的游戏像上面如许,那就是无解了。原理就像我们玩魔方一样,正常情况下不管我们打乱的多乱都可以还原,但是如果我们把 某几个块抠出来换换位置 ,那就大概还原不了了

网上也有许多算法可以避免天生无解的情况,但是写的都比较高深,加上自己阅读算法的本领有限,最后决定按照自己的思路实现。

我的思路实在比较简单,一句话总结就是逆向推理法,我们可以先从排列好的数组开始,然后随机的通过 移动 打乱其顺序,如许肯定可以包管天生的标题有解,同时可以根据打乱的步数来控制游戏的难度,真是个一石二鸟的idear。

源码实现


数据存放

起首我考虑的是利用一个一维数组来存放数据
  1. let arr = [1,2,3,4,5,6,7,8,0] // 0 代表空白
复制代码
但是如许会出现一个标题,就是移动的时候逻辑变的相当贫苦,由于一维数组只能记载数据并不能记载竖直方向的位置,这个时候我们自然就想到利用二维数组了,比如,当我们必要天生一个3*3的游戏时数据是如许的:
  1. let arr [
  2.   [1,2,3],
  3.   [4,5,6],
  4.   [7,8,0]
  5. ]
复制代码
如许我们就可以通过下面来模拟x,y轴来表现每个数字的位置。比如0的位置是(2,2),6的位置是(1,2),如果我想移动6和0的位置,只必要把他们的坐标做变动即可。

移动函数

数字华容道最关键的交互就是用户点击那个块就移动哪个块,但是必要注意的是只有0附近的数组可以移动。下面我们先完成移动函数
  1. function move(x, y, moveX, moveY) {
  2.   const num = state.arr[x][y];
  3.   state.arr[x][y] = state.arr[moveX][moveY];
  4.   state.arr[moveX][moveY] = num;
  5. }
复制代码
是不是很简单,实在就是把要移动的两个数的下标给交换下位置
有了移动函数,我们就可以实现上,下,左,右的移动了
  1. // 上移动
  2. function moveTop(x, y) {
  3.   if (x <= 0) return -1;
  4.   //  开始交换位置
  5.   const okx = x - 1;
  6.   move(x, y, okx, y);
  7.   return {
  8.    x: okx,
  9.    y,
  10.   };
  11. }
  12. //下移动
  13. function moveDown(x, y) {
  14.   if (x >= level - 1) return -1;
  15.   const okx = x + 1;
  16.   move(x, y, okx, y);
  17.   return {
  18.    x: okx,
  19.    y,
  20.   };
  21. }
  22. // 左移动
  23. function moveLeft(x, y) {
  24.   if (y <= 0) return -1;
  25.   const oky = y - 1;
  26.   move(x, y, x, oky);
  27.   return {
  28.    x,
  29.    y: oky,
  30.   };
  31. }
  32. // 右移动
  33. function moveRight(x, y) {
  34.   if (y >= level - 1) return -1;
  35.   const oky = y + 1;
  36.   move(x, y, x, oky);
  37.   return {
  38.    x,
  39.    y: oky,
  40.   };
  41. }
复制代码
现在我们再实现一个判定移动方向的方法,如下所示:
  1. function shouldMove(x, y) {
  2.   //  判断向哪移动
  3.   const { emptyX, emptyY } = seekEmpty();
  4.   if (x === emptyX && y !== emptyY && Math.abs(y - emptyY) === 1) {
  5.    // 说明在一个水平线上 可能是左右移动
  6.    if (y > emptyY) {
  7.     moveLeft(x, y);
  8.    } else {
  9.     moveRight(x, y);
  10.    }
  11.   }
  12.   if (y === emptyY && x !== emptyX && Math.abs(x - emptyX) === 1) {
  13.    // 说明需要上下移动
  14.    if (x > emptyX) {
  15.     moveTop(x, y);
  16.    } else {
  17.     moveDown(x, y);
  18.    }
  19.   }
  20. }
复制代码
if里面判定的意思是如果我们点击的块是空缺快或者不是和空缺快挨着的那个,那我们就不做任那边理

天生游戏面板

实在就是随机调用上移,下移,左移,右移函数,把数组打乱
  1. // 随机打乱
  2. function moveInit(diffic) {
  3.   state.arr = creatArr(level);
  4.   const num = diffic ? diffic : state.diffec;
  5.   const fns = [moveTop, moveDown, moveLeft, moveRight];
  6.   let Index = null;
  7.   let fn;
  8.   for (let i = 0; i < num; i++) {
  9.    Index = Math.floor(Math.random() * fns.length);
  10.    //  moveConsole(Index);
  11.    fn = fns[Index](startX, startY);
  12.    if (fn != -1) {
  13.     const { x, y } = fn;
  14.     startX = x;
  15.     startY = y;
  16.    }
  17.   }
  18. }
复制代码
短短几个函数,就完成了焦点逻辑,还有几个函数没有先容到比如判定游戏完成,探求空缺块的位置,创建二维数组各人可自行阅读源码

利用vue3重构

以上逻辑貌似和vue3没什么关系,但是我们忽略了最紧张的一点,就是 改变数组后,视图也就改变了 这不就是响应式吗,利用vue3后我们可以把关于游戏的所有逻辑放到一个js里面,大大镌汰了代码的耦合度
  1. const { arr, shouldMove, moveInit } = useCreateGame(
  2. gamedata.level,
  3. gamedata.difficulty,
  4. gameEndCallback
  5. );
复制代码
大概有的人会有疑问?把所有逻辑抽离出来这不是很正常的利用吗?岂非用vue2的时候都不能抽离了?
但是各人不要忘记了,我们的这个数组必要是响应式的,如果我们单独把逻辑抽离出来那我们在js文件里面改变数组还是响应式的吗

但当我们利用vue3的composition-api时就可以再js文件中声明一个响应式变量,且在组件中利用时它还是响应式的
  1. export default function useCreateGame() {
  2. //声明一个响应式变量
  3. ...
  4. const state = reactive({
  5.   arr: [],
  6. });
  7. ...
  8. return {
  9. ...toRefs(state)
  10. ...
  11. }
  12. }
复制代码
  1. const { arr, shouldMove, moveInit } = useCreateGame(
  2. gamedata.level,
  3. gamedata.difficulty,
  4. gameEndCallback
  5. );// 这个时候 arr 还是响应式的
复制代码
这正是composition-api强盛地点,有了composition-api我们可以恣意组装我们的逻辑代码了
在vue2 如果要维护一个响应式变量我们是不是就要利用vuex这种状态管理器了,如许就增加了代码的耦合度
关于vite2


现在vite已经到了vite2版本,而且官方还在飞快迭代中,利用vite2创建的项目默认是可以利用setup新特性的,比如我们可以如许写:
  1. <template>
  2. <div>
  3.   {{ name }}
  4. </div>
  5. </template>
  6. <script setup>
  7. import { ref } from "vue";
  8. const name = ref('"公众号码不停息"');
  9. </script>
复制代码
等价于如许写
  1. <template>
  2. <div>
  3.   {{ name }}
  4. </div>
  5. </template>
  6. <script>
  7. import { ref } from "vue";
  8. export default {
  9. setup() {
  10.   const name = ref("公众号码不停息");
  11.   return {
  12.    name,
  13.   };
  14. },
  15. };
  16. </script>
复制代码
看着就简单了许多,而且在setup版本中vue又出了几个api,感爱好的可以去官网看下,个人感觉还是挺香的。由于新语法还是实验性子的本次代码重构并未利用。

源码地点


源码地点:数字华容道拼图游戏 欢迎start

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作