0基础从前端到Web3 —— Mine Clearance Frontend(二) —— 在前篇的基础上继续往下,本篇主要是链上调用部分,让整个项目可以进行最基本的扫雷游戏。
在一的基础上继续往下,本篇主要是链上调用部分,让整个项目可以进行最基本的扫雷游戏。
$\mathit {Sui\ Move}$ 链上部署的自主实现的简单扫雷游戏可以点击查看,只不过这里将区域大小扩大为了 $\text {10}\ \times\ \text {20}$,更新了雷格判断是否存在雷的逻辑使其不至于越界报错,具体内容已经 $\mathit {push}$ 到了 $\mathit {Github}$,可以自行查看。
在前篇,点击 $\mathit {GAME}$ 按钮只是判断了是否连接到了 $\mathit {Sui}$ 钱包,在这里,我们往这个按钮的点击事件里额外调用两个函数,一个是清空当前的游戏区域,因为可能残存上一局的游戏内容,第二个则是链上调用,创建一局全新的游戏,并获取返回的链上对象GameInfo
的ID
。
回想前篇,我们是如何绘制雷格的?<br>将一个个按钮组合成ButtonGroup
再放到一起组合成 $\text {10}\ \times\ \text {20}$ 的区域。<br>同理,想要改变每一个按钮的内容,也需要通过循环取用加以修改。
为了让功能更具有通用性,再来考虑链上调用后得到的游戏内容变化会以何种形式呈现?<br>$\mathit {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();
}
这个函数会在按钮的点击事件中被调用,如果到了那个时候再从一些SDK
取值则会报错,所以我们需要事先声明,再通过参数的形式传入。
const account = useCurrentAccount();
const { mutate: signAndExecuteTransactionBlock } = useSignAndExecuteTransactionBlock();
const [gameInfoID, setGameInfoID] = React.useState("");
通过前前篇的文章可以知道,这里的链上调用其实跟直接用 $\mathit {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
的变量就已经被设置成了我们所需要的值。
在前篇当中,雷区的按钮点击逻辑就是显现一行提示语,再将其填充进 $\mathit {X}$,在这里,我们完善这一逻辑。
第一步,点击相当于链上调用,其中有个关键的参数就是需要知道玩家点击的是第几行第几列的方格。<br>在按钮中,有一个button-key
的属性,可以通过点击事件的event.target.getAttribute('button-key')
取得,如果是其父节点则是const l = event.target.parentElement.getAttribute('button-key')
。<br>我们将这两个值,按照按钮排列方式,一个设成行,一个设成列(生成区域时从循环的i, j
来赋值),此时取出转换为数字类型传参即可。
已知点击过后的游戏区域的详细信息会通过链上事件的方式触发,那么这第二步就是从这信息当中截取我们所需要的,也就是从中找出存储有雷区揭示情形的字符串数组,通过上面的方法将其显现到前端。<br>看过该合约的一定也知道,一旦游戏成功或者失败,是会额外触发一个对应的事件的,这一点就可以用来判断该显示哪一句提示(胜利、继续、失败)。
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");
}
}
);
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!