在一的基礎上繼續往下,本篇主要是鏈上調用部分,讓整個項目可以進行最基本的掃雷游戲。
S u i M o v e \mathit {Sui\ Move} Sui?Move 鏈上部署的自主實現的簡單掃雷游戲可以點擊查看,只不過這里將區域大小擴大為了 10? × 20 \text {10}\ \times\ \text {20} 10?×?20,更新了雷格判斷是否存在雷的邏輯使其不至于越界報錯,具體內容已經 p u s h \mathit {push} push 到了 G i t h u b \mathit {Github} Github,可以自行查看。
一:創建游戲
在前篇,點擊 G A M E \mathit {GAME} GAME 按鈕只是判斷了是否連接到了 S u i \mathit {Sui} Sui 錢包,在這里,我們往這個按鈕的點擊事件里額外調用兩個函數,一個是清空當前的游戲區域,因為可能殘存上一局的游戲內容,第二個則是鏈上調用,創建一局全新的游戲,并獲取返回的鏈上對象GameInfo
的ID
。
1.1 ClearCheckerboard
回想前篇,我們是如何繪制雷格的?
將一個個按鈕組合成ButtonGroup
再放到一起組合成 10? × 20 \text {10}\ \times\ \text {20} 10?×?20 的區域。
同理,想要改變每一個按鈕的內容,也需要通過循環取用加以修改。
為了讓功能更具有通用性,再來考慮鏈上調用后得到的游戲內容變化會以何種形式呈現?
S u i M o v e \mathit {Sui\ Move} Sui?Move 的合約代碼中會對每一次游戲點擊觸發一個事件,不管游戲成功、失敗還是繼續游戲,都會將最新的方格信息傳回來。前端通過這一事件獲得當前的雷區詳情,而這一信息會以一個字符串數組的形式呈現。
也就是說,我們需要構建一個內容為空的字符串數組,再將其視為當前雷區的實際情形對每一個按鈕的內容進行循環遍歷刪改。
function ChangeCheckerboard(checkerboard: any) {// console.log(checkerboard);// for (let row of checkerboard) {// console.log(row);// }const htmlCheckerboard = document.getElementById("Checkerboard")!.children[0].children;let i = 0, j = 0;for (let list of htmlCheckerboard) {// console.log(list);for (let row of list.children) {// console.log(row);// console.log(checkerboard[i][j]);const replace = checkerboard[i][j] !== "-" ? checkerboard[i][j] : " ";ChangeChess(row, replace);i += 1;}j += 1;i = 0;}
}function ChangeChess(html: Element, replace: string) {// console.log(html.innerHTML);const str1 = html.innerHTML.split('<', 1)[0];const str2 = html.innerHTML.substring(str1.length);html.innerHTML = replace.concat(str2);
}function ClearCheckerboard() {const checkerboard = [];for (let i = 0; i < MaxRow; i++) {let rowStr = "";for (let j = 0; j < MaxList; j++)rowStr = rowStr.concat("-");checkerboard.push(rowStr);}ChangeCheckerboard(checkerboard);HiddenFeedBack();
}
1.2 MoveCallStartGame
這個函數會在按鈕的點擊事件中被調用,如果到了那個時候再從一些SDK
取值則會報錯,所以我們需要事先聲明,再通過參數的形式傳入。
const account = useCurrentAccount();
const { mutate: signAndExecuteTransactionBlock } = useSignAndExecuteTransactionBlock();
const [gameInfoID, setGameInfoID] = React.useState("");
通過前前篇的文章可以知道,這里的鏈上調用其實跟直接用 T y p e S c r i p t \mathit {TypeScript} TypeScript 來調用沒什么太大的區別,都是往TransactionBlock
里面填充一筆筆交易信息,再signAndExecuteTransactionBlock
簽名提交交易塊到鏈上執行,重點是如何對成功交易后返回的信息篩選處理,取得我們所需要的值。
首先,需要在signAndExecuteTransactionBlock
中,將showObjectChanges
設置為true
,因為我們的目的是取得某一個對象的ID
;接著可以通過onSuccess
來截取返回的信息,如果不確定其結構,就將其console.log
出來查看,最后不斷通過.
層層深入取用。
function MoveCallStartGame(signAndExecuteTransactionBlock: any, setGameInfoID: any) {const txb = new TransactionBlock();const [coin] = txb.splitCoins(txb.gas, [666]);txb.moveCall({target: `${Package}::player::start_game`,arguments: [txb.object(GameCap),coin,]});signAndExecuteTransactionBlock({transactionBlock: txb,chain: `sui:$network`,options: {showObjectChanges: true,}},{onSuccess: (result: any) => {// console.log(result.objectChanges);for (let obj of result.objectChanges) {// console.log(obj);if (obj.type === "created") {// console.log(obj.objectId);setGameInfoID(obj.objectId);break;}}}});
}
一切順利的情況下,在編寫該按鈕點擊事件的函數當中,一個名為gameInfoID
的變量就已經被設置成了我們所需要的值。
二:享受游戲
在前篇當中,雷區的按鈕點擊邏輯就是顯現一行提示語,再將其填充進 X \mathit {X} X,在這里,我們完善這一邏輯。
第一步,點擊相當于鏈上調用,其中有個關鍵的參數就是需要知道玩家點擊的是第幾行第幾列的方格。
在按鈕中,有一個button-key
的屬性,可以通過點擊事件的event.target.getAttribute('button-key')
取得,如果是其父節點則是const l = event.target.parentElement.getAttribute('button-key')
。
我們將這兩個值,按照按鈕排列方式,一個設成行,一個設成列(生成區域時從循環的i, j
來賦值),此時取出轉換為數字類型傳參即可。
已知點擊過后的游戲區域的詳細信息會通過鏈上事件的方式觸發,那么這第二步就是從這信息當中截取我們所需要的,也就是從中找出存儲有雷區揭示情形的字符串數組,通過上面的方法將其顯現到前端。
看過該合約的一定也知道,一旦游戲成功或者失敗,是會額外觸發一個對應的事件的,這一點就可以用來判斷該顯示哪一句提示(勝利、繼續、失敗)。
function MoveCallGameClick(r: number, l: number, gameInfoID: string, signAndExecuteTransactionBlock: any) {const txb = new TransactionBlock();txb.moveCall({target: `${Package}::player::game_click`,arguments: [txb.pure(r),txb.pure(l),txb.object(gameInfoID),]});signAndExecuteTransactionBlock({transactionBlock: txb,chain: `sui:$network`,options: {showEvents: true,}},{onSuccess: (result: any) => {// console.log(result);let showed = false;for (let event of result.events) {if (event.type === GameEvent) {// console.log(event.parsedJson.checkerboard);ChangeCheckerboard(event.parsedJson.checkerboard);} else if (event.type === GameSuccessEvent) {ShowFeedBack("success_alert");showed = true;} else {ShowFeedBack("failure_alert");showed = true;}}if (!showed)ShowFeedBack("encourage_alert");}});
}
三:實機演示
四:加入組織,共同進步!
- Sui 中文開發群(TG)
- M o v e \mathit{Move} Move 語言學習交流群: 79489587