使用vue3重構拼圖遊戲的實現示例
前言
花瞭兩天時間,重構瞭項目中的一個拼圖小遊戲(又名數字華容道),為瞭方便使用抽離成瞭獨立組件,效果如下:
線上體驗
源碼地址在文章最後哦!
主要重構點
原有拼圖遊戲是通過開源代碼加以改造,使用的是 vue2 。在實際項目使用一切正常,但還是存在以下痛點
- 源代碼臃腫,暴露的配置項不足,特備是和項目現有邏輯結合時體現的更加明顯
- 生成的遊戲可能出現無解情況,為瞭避免無解,隻好寫死幾種情況然後隨機生成
- 源代碼是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這種狀態管理器瞭,這樣就增加瞭代碼的耦合度
關於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😍
最後
你可能感興趣:
基於vue-router思考🕓實現一個簡易版vue-router
基於webpack打包多頁應用,對前端工程化的思考
到此這篇關於使用vue3重構拼圖遊戲的實現示例的文章就介紹到這瞭,更多相關vue3重構拼圖內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 詳解Vue3中setup函數的使用教程
- 詳解vue3.2中setup語法糖<script lang="ts" setup>
- vue3常用的API使用簡介
- Vue3 Composition API的使用簡介
- Vue3+script setup+ts+Vite+Volar搭建項目