新聞中心
利用vue3怎么重構(gòu)一個(gè)拼圖游戲?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
原有拼圖游戲是通過(guò)開(kāi)源代碼加以改造,使用的是 vue2 。在實(shí)際項(xiàng)目使用一切正常,但還是存在以下痛點(diǎn)
源代碼臃腫,暴露的配置項(xiàng)不足,特備是和項(xiàng)目現(xiàn)有邏輯結(jié)合時(shí)體現(xiàn)的更加明顯
生成的游戲可能出現(xiàn)無(wú)解情況,為了避免無(wú)解,只好寫(xiě)死幾種情況然后隨機(jī)生成
源代碼是vue2版本,不支持vue3
最后決定使用 vue3 重新實(shí)現(xiàn)拼圖游戲,著重注意以下細(xì)節(jié)
組件使用起來(lái)足夠簡(jiǎn)單
可以自定義游戲難度
支持圖片和數(shù)組兩種模式
實(shí)現(xiàn)思路
無(wú)論是拼圖片還是拼數(shù)字,其原理都是要把原本打亂的數(shù)組移動(dòng)成有序狀態(tài)。網(wǎng)上也有很多實(shí)現(xiàn)數(shù)字華容的的算法,算法主要需要解決的就是如何生成一組 隨機(jī)且有解 的數(shù)組,有的人可能有疑問(wèn),數(shù)組華容道還有可能無(wú)解嗎?
如果生成的游戲像上面這樣,那就是無(wú)解了。原理就像我們玩魔方一樣,正常情況下不管我們打亂的多亂都可以還原,但是如果我們把 某幾個(gè)塊摳出來(lái)?yè)Q換位置 ,那就可能還原不了了
網(wǎng)上也有很多算法可以避免生成無(wú)解的情況,但是寫(xiě)的都比較高深,加上自己閱讀算法的能力有限,最后決定按照自己的思路實(shí)現(xiàn)。
我的思路其實(shí)比較簡(jiǎn)單,一句話總結(jié)就是逆向推理法,我們可以先從排列好的數(shù)組開(kāi)始,然后隨機(jī)的通過(guò) 移動(dòng) 打亂其順序,這樣肯定可以保證生成的題目有解,同時(shí)可以根據(jù)打亂的步數(shù)來(lái)控制游戲的難度,真是個(gè)一舉兩得的idear。
源碼實(shí)現(xiàn)
數(shù)據(jù)存放
首先我考慮的是使用一個(gè)一維數(shù)組來(lái)存放數(shù)據(jù)
let arr = [1,2,3,4,5,6,7,8,0] // 0 代表空白
但是這樣會(huì)出現(xiàn)一個(gè)問(wèn)題,就是移動(dòng)的時(shí)候邏輯變的相當(dāng)麻煩,因?yàn)橐痪S數(shù)組只能記錄數(shù)據(jù)并不能記錄豎直方向的位置,這個(gè)時(shí)候我們自然就想到使用二維數(shù)組了,比如,當(dāng)我們需要生成一個(gè)3*3的游戲時(shí)數(shù)據(jù)是這樣的:
let arr [ [1,2,3], [4,5,6], [7,8,0] ]
這樣我們就可以通過(guò)下面來(lái)模擬x,y軸來(lái)表示每個(gè)數(shù)字的位置。比如0的位置是(2,2),6的位置是(1,2),如果我想移動(dòng)6和0的位置,只需要把他們的坐標(biāo)做調(diào)換即可。
移動(dòng)函數(shù)
數(shù)字華容道最關(guān)鍵的交互就是用戶點(diǎn)擊那個(gè)塊就移動(dòng)哪個(gè)塊,但是需要注意的是只有0附近的數(shù)組可以移動(dòng)。下面我們先完成移動(dòng)函數(shù)
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; }
是不是很簡(jiǎn)單,其實(shí)就是把要移動(dòng)的兩個(gè)數(shù)的下標(biāo)給交換下位置
有了移動(dòng)函數(shù),我們就可以實(shí)現(xiàn)上,下,左,右的移動(dòng)了
// 上移動(dòng) function moveTop(x, y) { if (x <= 0) return -1; // 開(kāi)始交換位置 const okx = x - 1; move(x, y, okx, y); return { x: okx, y, }; } //下移動(dòng) function moveDown(x, y) { if (x >= level - 1) return -1; const okx = x + 1; move(x, y, okx, y); return { x: okx, y, }; } // 左移動(dòng) function moveLeft(x, y) { if (y <= 0) return -1; const oky = y - 1; move(x, y, x, oky); return { x, y: oky, }; } // 右移動(dòng) function moveRight(x, y) { if (y >= level - 1) return -1; const oky = y + 1; move(x, y, x, oky); return { x, y: oky, }; }
現(xiàn)在我們?cè)賹?shí)現(xiàn)一個(gè)判斷移動(dòng)方向的方法,如下所示:
function shouldMove(x, y) { // 判斷向哪移動(dòng) const { emptyX, emptyY } = seekEmpty(); if (x === emptyX && y !== emptyY && Math.abs(y - emptyY) === 1) { // 說(shuō)明在一個(gè)水平線上 可能是左右移動(dòng) if (y > emptyY) { moveLeft(x, y); } else { moveRight(x, y); } } if (y === emptyY && x !== emptyX && Math.abs(x - emptyX) === 1) { // 說(shuō)明需要上下移動(dòng) if (x > emptyX) { moveTop(x, y); } else { moveDown(x, y); } } }
if里面判斷的意思是如果我們點(diǎn)擊的塊是空白快或者不是和空白快挨著的那個(gè),那我們就不做任何處理
生成游戲面板
其實(shí)就是隨機(jī)調(diào)用上移,下移,左移,右移函數(shù),把數(shù)組打亂
// 隨機(jī)打亂 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; } } }
短短幾個(gè)函數(shù),就完成了核心邏輯,還有幾個(gè)函數(shù)沒(méi)有介紹到比如判斷游戲完成,尋找空白塊的位置,創(chuàng)建二維數(shù)組大家可自行閱讀源碼
使用vue3重構(gòu)
以上邏輯貌似和vue3沒(méi)什么關(guān)系,但是我們忽略了最重要的一點(diǎn),就是 改變數(shù)組后,視圖也就改變了 這不就是響應(yīng)式嗎,使用vue3后我們可以把關(guān)于游戲的所有邏輯放到一個(gè)js里面,大大減少了代碼的耦合度
const { arr, shouldMove, moveInit } = useCreateGame( gamedata.level, gamedata.difficulty, gameEndCallback );
可能有的人會(huì)有疑問(wèn)?把所有邏輯抽離出來(lái)這不是很正常的操作嗎?難道用vue2的時(shí)候都不能抽離了?
但是大家不要忘記了,我們的這個(gè)數(shù)組需要是響應(yīng)式的,如果我們單獨(dú)把邏輯抽離出來(lái)那我們?cè)趈s文件里面改變數(shù)組還是響應(yīng)式的嗎
但當(dāng)我們使用vue3的composition-api時(shí)就可以再js文件中聲明一個(gè)響應(yīng)式變量,且在組件中使用時(shí)它還是響應(yīng)式的
export default function useCreateGame() { //聲明一個(gè)響應(yīng)式變量 ... const state = reactive({ arr: [], }); ... return { ...toRefs(state) ... } }
const { arr, shouldMove, moveInit } = useCreateGame( gamedata.level, gamedata.difficulty, gameEndCallback ); // 這個(gè)時(shí)候 arr 還是響應(yīng)式的
這正是composition-api強(qiáng)大所在,有了composition-api我們可以任意組裝我們的邏輯代碼了
在vue2 如果要維護(hù)一個(gè)響應(yīng)式變量我們是不是就要使用vuex這種狀態(tài)管理器了,這樣就增加了代碼的耦合度
關(guān)于vite2
現(xiàn)在vite已經(jīng)到了vite2版本,并且官方還在飛快迭代中,使用vite2創(chuàng)建的項(xiàng)目默認(rèn)是可以使用setup新特性的,比如我們可以這樣寫(xiě):
{{ name }}
等價(jià)于這樣寫(xiě)
{{ name }}
看完上述內(nèi)容,你們掌握利用vue3怎么重構(gòu)一個(gè)拼圖游戲的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
名稱欄目:利用vue3怎么重構(gòu)一個(gè)拼圖游戲-創(chuàng)新互聯(lián)
鏈接地址:http://www.dlmjj.cn/article/dghigj.html