使用vue3重構拼圖遊戲,真香!

前言

花了兩天時間,重構了項目中的一個拼圖小遊戲(又名數字華容道),爲了方便使用抽離成了獨立組件,效果以下:javascript

線上體驗前端

源碼地址在文章最後哦!vue

主要重構點

原有拼圖遊戲是經過開源代碼加以改造,使用的是 vue2 。在實際項目使用一切正常,但仍是存在如下痛點java

  • 源代碼臃腫,暴露的配置項不足,特備是和項目現有邏輯結合時體現的更加明顯
  • 生成的遊戲可能出現無解狀況,爲了不無解,只好寫死幾種狀況而後隨機生成
  • 源代碼是vue2版本,不支持vue3

最後決定使用 vue3 從新實現拼圖遊戲,着重注意如下細節react

  1. 組件使用起來足夠簡單
  2. 能夠自定義遊戲難度
  3. 支持圖片和數組兩種模式

實現思路

不管是拼圖片仍是拼數字,其原理都是要把本來打亂的數組移動成有序狀態。網上也有不少實現數字華容的的算法,算法主要須要解決的就是如何生成一組 隨機且有解 的數組,有的人可能有疑問,數組華容道還有可能無解嗎?webpack

若是生成的遊戲像上面這樣,那就是無解了。原理就像咱們玩魔方同樣,正常狀況下無論咱們打亂的多亂均可以還原,可是若是咱們把 某幾個塊摳出來換換位置 ,那就可能還原不了了git

網上也有不少算法能夠避免生成無解的狀況,可是寫的都比較高深,加上本身閱讀算法的能力有限,最後決定按照本身的思路實現。github

個人思路其實比較簡單,一句話總結就是逆向推理法,咱們能夠先從排列好的數組開始,而後隨機的經過 移動 打亂其順序,這樣確定能夠保證生成的題目有解,同時能夠根據打亂的步數來控制遊戲的難度,真是個一箭雙鵰的idear。web

源碼實現

數據存放

首先我考慮的是使用一個一維數組來存放數據算法

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這種狀態管理器了,這樣就增長了代碼的耦合度

關於vite2

如今vite已經到了vite2版本,而且官方還在飛快迭代中,使用vite2建立的項目默認是可使用setup新特性的,好比咱們能夠這樣寫:

<template>
  <div> {{ name }} </div>
</template>

<script setup> import { ref } from "vue"; const name = ref('"公衆號碼不停息"'); </script>
複製代碼

等價於這樣寫

<template>
  <div> {{ name }} </div>
</template>
<script> import { ref } from "vue"; export default { setup() { const name = ref("公衆號碼不停息"); return { name, }; }, }; </script>
複製代碼

看着就簡單了不少,而且在setup版本中vue又出了幾個api,感興趣的能夠去官網看下,我的感受仍是挺香的。由於新語法仍是實驗性質的本次代碼重構並未使用。

源碼地址

源碼地址: 數字華容道拼圖遊戲 歡迎start😍

最後

你可能感興趣:

若是幫助,歡迎點贊哦😁

相關文章
相關標籤/搜索