iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0

前言

因為玩接龍有時會有按錯步,這時候沒有返回上一步的機制就只能硬著頭皮玩下去或按重置,今天想解決這個問題。

開發前的思考

ReactJS官方學習文件中OOXX遊戲Tic-Tac-Toe也有提到時光旅行的實作,基本上就是每一步更動後的結果狀態都推入(Push)陣列變數history裡面,時光回朔就是將結果狀態直接設為history[index]

目前開發下來的程式碼大概要回到上一步的只有分數牌組發牌的索引
經過的時間應該就不用上一步

實作過程

儲存遊戲變化的歷史

首先宣告一個負責儲存歷史的ref變數history

const history = ref([]);

宣告函數pushStateToHistory()負責撰寫把最新的狀態推入history

  • 累積超過30個後會將最舊的狀態移除再推入最新的狀態,避免暫存太多的上一步驟。
  • 因為reactive的關係所以不得不手動深度複製每張卡牌,這也是為什麼要做elemFunc的原因
/** 儲存當前狀態到歷史紀錄 */
function pushStateToHistory() {
    if (history.value.length > 30) {
        const startIndex = history.value.length - 30;
        history.value = history.value.slice(startIndex, history.value.length);
    }
    const elemFunc = (card) => ({
        "value": card.value,
        "isOpen": card.isOpen,
        "isDone": card.isDone,
    });
    history.value = [
        ...history.value,
        {
            "cardStacks": {
                first: cardStacks.first.slice().map(elemFunc),
                second: cardStacks.second.slice().map(elemFunc),
                third: cardStacks.third.slice().map(elemFunc),
                fourth: cardStacks.fourth.slice().map(elemFunc),
                fifth: cardStacks.fifth.slice().map(elemFunc),
                sixth: cardStacks.sixth.slice().map(elemFunc),
                seventh: cardStacks.seventh.slice().map(elemFunc),
                dealerStacks: cardStacks.dealerStacks.slice().map(elemFunc),
                club: cardStacks.club.slice().map(elemFunc),
                diamond: cardStacks.diamond.slice().map(elemFunc),
                heart: cardStacks.heart.slice().map(elemFunc),
                spade: cardStacks.spade.slice().map(elemFunc),
            },
            "gameScore": JSON.parse(JSON.stringify(gameScore.value)),
            "dealer": { index: dealer.index },
        }
    ];
}

接著在程式碼中【分數、卡牌有變動】的情況都補上執行pushStateToHistory();去儲存當下的狀態:

  • 發牌區移動/結算牌堆移動/7牌堆移動成功移動後
  • 遊戲初始化resetGame
  • 連點自動拖曳clickAutoMove只要成功移動,最後也會執行

發牌區<DealerArea />點擊開牌也會執行pushStateToHistory();,因為發牌索引dealer.index產生變化

實作返回上一步函數

返回上一步函數undo的演算法:

  1. 取得history變數的陣列的長度,若長度大於1繼續往下執行,否則不執行後續步驟。
  2. 因為history目前最後一個元素就是當下的狀態,所以從history克隆出原本長度減一的陣列暫存至變數prevHistory
  3. history的值更新為prevHistory
  4. prevHistory最後一個元素內的狀態發牌區/結算牌堆/7牌堆/遊戲分數/發牌索引的值全都覆蓋到對應變數
/** 返回上一步 */
function undo() {
    if (history.value.length > 1) {
        const prevHistory = history.value.slice(0, history.value.length - 1);
        history.value = prevHistory;
        const prevState = prevHistory[prevHistory.length - 1];
        cardStacks.dealerStacks = prevState.cardStacks.dealerStacks;
        FOUR_SUITS.forEach((deckName) => {
            cardStacks[deckName] = prevState.cardStacks[deckName];
        })
        SEVEN_STACKS.forEach((deckName) => {
            cardStacks[deckName] = prevState.cardStacks[deckName];
        })
        gameScore.value = prevState.gameScore;
        dealer = prevState.dealer;
    }
}

小結

今日除了完成返回上一步的功能使得遊戲玩起來更有趣😎也盡可能以演算法的角度更明確的步驟去說明程式碼的實現。

程式碼: https://github.com/kabuto412rock/ithelp-pokergame/tree/day26

Yes

參考


上一篇
Day25 紙牌接龍-結算畫面採用Modal和修正移牌優先權
下一篇
Day27 當牌全開時自動結算
系列文
毫無自信的開發Web遊戲接龍-成為創造龍的英雄之路!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言