从零开始完整开发基于websocket的在线对弈游戏【五子棋】,只用几十行代码完成全部逻辑。
五子棋是規(guī)則簡單明了的策略型游戲,先形成五子連線者獲勝。
本課程習(xí)作采用兩人在線對弈的方式進(jìn)行比賽,拿著手機在上下班路上玩特別合適。
整個過程在眾觸低代碼應(yīng)用平臺進(jìn)行,使用表達(dá)式描述游戲邏輯(高度簡化版JS)。
本課程重點學(xué)習(xí)websocket實時消息的發(fā)送與接收處理。
兩人在線下棋演示
?
先動手玩一玩:https://gobang.zc-app.cn
因為是在線游戲,需要登錄,可以用手機和郵箱分別注冊,用電腦和手機自己跟自己玩。
URL后面加/z就進(jìn)入開發(fā)模式:https://gobang.zc-app.cn/z
詳盡的的教學(xué)請移步嗶哩嗶哩視頻:【眾觸課程】五子棋_嗶哩嗶哩_bilibili
棋盤結(jié)構(gòu)
$v.棋盤 = array(14, array(14, ""))用嵌套數(shù)據(jù)組件使用14 * 14的二維數(shù)組渲染而成,即圖中畫黑線的格子。
方陣結(jié)構(gòu)
$v.方陣 = array(15, array(15, ""))用嵌套數(shù)據(jù)組件使用15 * 15的二維數(shù)組渲染而成,即圖中黑線交叉的點,鼠標(biāo)hover在組件上時高亮的圓圈。
棋手狀態(tài)
undefined、邀請中、對方出棋、己方出棋
默認(rèn)就是沒有值,表示還沒開始下棋或者結(jié)束了(勝負(fù)已分)。對方出棋的時候己方不能落子。
賬號登錄
既然是在線玩的,就要求登錄。可以用手機或郵箱注冊。
登錄后馬上打開連接,有玩家上線了,有玩家邀請了,對方落子了都是通過此連接socket即時通知的。
打開連接socket:onLogin
$socket.open($c.exp, { channels: ["比賽"], onOnline: true, onOffline: true, allowMultiLogin: true })第一個參數(shù)$c.exp是個對象,可以包含onConnect, onData, onReconnect, onError,前兩個是必須的。
第二個參數(shù)是option選項,channels數(shù)組里放你需關(guān)注的頻道,onOnline表示有人上線時是否要通知到你,onOffline則是有人下線是要否通知,allowMultiLogin表示同一個賬號能否能在多個地方登錄而不強制前面登錄的賬號下線。
連上后:onConnect
$socket.onlines(["比賽"]) $v.onlines = $r.比賽.filter('$x !== $c.me._id') $v.onlines.forEach('$user.get($x)') render()連上后查詢一下關(guān)注"比賽"頻道的在線玩家,排除自己后放到$v.onlines列表后依次獲取用戶信息。
消息格式
下面有多個on開頭的表達(dá)式都是只收到socket消息。它們都有共同的格式:type是消息類型,x是消息體,from是消息發(fā)送人。消息接受人to和發(fā)送時間d在此案例中未使用到。
有人上線了:onOnline
$v.onlines.push(x) $v.onlines = $v.onlines.unique() $user.get(x)把上線的人放入上面的$v.onlines中,并去重。
有人斷線了:onOffline
$v.對手 === x ? alert("對方斷線了") : "" $v.onlines.splice($v.onlines.indexOf(x), 1)斷線了就把他/她從$v.onlines移除。如果剛好是正在跟你對弈的棋手則拋出一個警告通知。
收到數(shù)據(jù)后:onData
stopIf($c.me._id == from) $c.exp[type].exc() render()先要排除是自己發(fā)出的數(shù)據(jù),因為socket是廣播消息的,自己也能收到。
然后再根據(jù)消息類型執(zhí)行對應(yīng)的表達(dá)式,可能的類型有:on被邀、on拒邀、on受邀、on落子。
當(dāng)其他人登錄時,【對手】右邊的問號圓圈就會閃爍,點擊它會彈出在線玩家列表,從中選擇一個可發(fā)出對弈邀請。
發(fā)出對弈邀請
$socket.send($x, "on被邀", "邀請") $v.狀態(tài) = "邀請中" info("邀請已發(fā)出,請等待對方接受邀請")收到消息:on被邀
stopIf($v.狀態(tài), '$socket.send(from, "on拒邀", "對方正在下棋")') $user.get(from) $v.對手 = from $v.pop = "選棋子"如果自己正在下棋就直接發(fā)出"on拒邀"消息,拒絕邀請。
獲取對方用戶信息,彈出模態(tài)窗口提示接受要是拒絕邀請。
收到消息:on拒邀
$v.狀態(tài) = undefined warn(x || "對方拒絕你的邀請")把前面的”邀請中“的狀態(tài)置空,彈出對方發(fā)來的拒邀消息
選子
$v.己方 = "白" // "黑" $c.exp.受邀.exc()接受邀請:受邀
$socket.send($v.對手, "on受邀", $v.己方) $v.方陣 = array(15, array(15, "")) $v.pop = undefined $v.對方 = ($v.己方 === "黑" ? "白" : "黑") $v.狀態(tài) = "己方出棋" info("請出棋")給對方發(fā)送“on被邀“消息,捎上自己選的子。
清空方陣,準(zhǔn)備出棋。
收到消息:on被邀
$v.方陣 = array(15, array(15, "")) $v.對手 = from $v.對方 = x $v.己方 = (x === "黑" ? "白" : "黑") $v.狀態(tài) = "對方出棋" info("對方已接受邀請,請等待對方先出棋")from是對手用戶ID,x是對方選的子,自己就只能選另一種子了。
落子
stopIf($v.狀態(tài) !== "己方出棋" || $v.方陣[$parent.$index][$index] || $v.連續(xù)棋子.length > 4) $v.落子點 = [$parent.$index, $index] $socket.send($v.對手, "on落子", $v.落子點) $("." + $v.己方 + "子聲音").play() $v.方陣[$parent.$index][$index] = $v.己方 $v.檢查方向.forEach($c.exp.落_是否勝出) $v.狀態(tài) = "對方出棋"如果不是己方出棋的狀態(tài),或者落子位置不在方陣內(nèi),或者已經(jīng)組成4個以上連續(xù)棋子都不可落子。
發(fā)出"on落子"消息,捎上剛才的落子點坐標(biāo)軸。
播放落子聲音,并把己方棋子放在方陣的落子點上,并通過動態(tài)類名發(fā)出光暈。
檢查剛才的落子能否勝出。
檢查勝出(形成五子連線)
要判斷勝負(fù)只需落子時從落子點 [y, x] 以四種連線的正反方向分別查看,累計4個以上連續(xù)同色棋子為聲。
$v.檢查方向
[[[-1, 0],[1, 0]],[[0, -1],[0, 1]],[[1, -1],[-1, 1]],[[-1, -1],[1, 1]] ]-1表示往后檢查,0表示不動,1表示往前檢查。比如[-1, 0]是是X軸上往負(fù)值方向檢查,即正西方向;[1, -1]表示先往X軸正方向檢查再往Y軸負(fù)方向檢查,即東北方向。
即?
落子是否勝出
$v.連續(xù)棋子 = [$v.落子點] $l.方向 = $x[0] $l.非連續(xù) = false $v.循環(huán)4次.forEach($c.exp.落_相鄰?fù)? $l.方向 = $x[1] $l.非連續(xù) = false $v.循環(huán)4次.forEach($c.exp.落_相鄰?fù)? stopIf($v.連續(xù)棋子.length > 4, 'info(($v.狀態(tài) === "己方出棋" ? $v.己方 : $v.對方) + "子贏了"); $v.狀態(tài) = undefined;')先把當(dāng)前落子位置作為第一個連續(xù)棋子,先往$v.檢查方向提供的一對方向的第一個方向試探移動4次(即循環(huán)4遍)看是否有相鄰?fù)?#xff0c;再往另一個方向也試探4次。
如果試探得到的$v.連續(xù)棋子大于4個,那當(dāng)前落子方勝出。
檢查與落子相鄰的同色子
$l.y = $v.落子點[0] + $l.方向[0] * $x $l.x = $v.落子點[1] + $l.方向[1] * $x !$l.非連續(xù) && $v.方陣[$l.y][$l.x] === ($v.狀態(tài) === "己方出棋" ? $v.己方 : $v.對方) ? $v.連續(xù)棋子.push([$l.y, $l.x]) : $l.非連續(xù) = true一個試探方向包括X軸方向和Y軸方向,有-1、0、1三種移法,分別移動一下坐標(biāo),檢查新坐標(biāo)在方陣中的棋子,如果坐標(biāo)上有子,并且現(xiàn)在是己方出棋而且這個子正好是己方顏色,那這個子就是連續(xù)棋子的一部分。其它情況都不能算連續(xù)同色子,比如坐標(biāo)上沒有子,或者是對方的子,再或者是以前就已經(jīng)非連續(xù)了,這次就沒必要繼續(xù)檢查了。
勝出的連續(xù)5個棋子也要發(fā)出光暈。前面新落的子已經(jīng)通過動態(tài)類名發(fā)出光暈,現(xiàn)在要找出連續(xù)棋子的其它棋子。
$v.連續(xù)棋子.length > 4 && $v.連續(xù)棋子.find('$x[0] === $ext.$parent.$index && $x[1] === $ext.$index') ? "光暈" : ""我們從連續(xù)棋子里面找,看看里面是否有一個棋子的坐標(biāo)跟當(dāng)前檢查的坐標(biāo)位置相同。$x[0]是連續(xù)棋子X坐標(biāo),$x[1]是Y坐標(biāo)。注意,這里是嵌套數(shù)據(jù)組件里作為動態(tài)類名的,$index是當(dāng)前數(shù)據(jù)組件的下標(biāo),$parent.$index是上一層數(shù)據(jù)組件的下標(biāo)。但由于它們是放在find()函數(shù)里面的,需要在前面添加$ext.表示它們函數(shù)外面上下文提供的數(shù)據(jù),如果沒有$ext.,那就成了find()函數(shù)提供給的上下文數(shù)據(jù)了。
準(zhǔn)備深入研究的同學(xué)請到五子棋在線對弈頁面后,點擊右側(cè)的【克隆】按鈕,把整個游戲復(fù)制一份隨意玩弄更改。
更多教學(xué)視頻請移步嗶哩嗶哩空間:眾觸應(yīng)用平臺的個人空間_嗶哩嗶哩_Bilibili,里面不僅有各種前端可視化案例演示和講解,還有多個完整功能的網(wǎng)站應(yīng)用案例的開發(fā)過程演示和講解。
總結(jié)
以上是生活随笔為你收集整理的从零开始完整开发基于websocket的在线对弈游戏【五子棋】,只用几十行代码完成全部逻辑。的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 杜比编码器ac3
- 下一篇: QQ第三方登陆的那些坑(不同应用的用户o