大家好,我卡頌。
中秋節在家無聊,想整點兒好玩的。思來想去決定做個用彈幕控制的坦克大戰。
具體邏輯是:
- 監聽直播間水友們的彈幕
- 將彈幕中有效的指令提取出來
- 將指令轉化為鍵盤按鍵在
坦克大戰
中輸入 - 直播
坦克大戰
遊戲畫面
這樣就實現從操作
到展示
的完整邏輯,所有直播間的水友都可以參與遊戲,下面是實際效果:
讓我們來看看技術細節吧。
監聽水友們的彈幕
這一步我使用puppeteer
監聽我直播間的DOMNodeInserted
事件。
DOMNodeInserted
事件在一個節點作為子節點被插入到另一個節點中時觸發
當觸發後,根據類名篩選出屬於彈幕的節點。
不得不說,B站彈幕資料真的很好抓,都存在$('.chat-item.danmaku-item').dataset()
中了。
指令識別
抓取出彈幕內容後,需要些額外處理,比如:
- 12345代表上下左右 開炮,需要識別出帶這些數字的彈幕
- 奇數暱稱長度的水友的彈幕控制玩家1的坦克,偶數控制玩家2的坦克
- 處理同一時間多人發彈幕的情況
我的處理方法是:設定一個時間間隔(200ms),統計在此時間內收到各種指令的數量。
200ms到期後出現次數最多的指令作為最終指令。
魔改坦克大戰
接下來我開始尋找開源的坦克大戰,這個倉庫的star
最多:battle-city
開始我以為作者是用canvas
實現的遊戲,但是當我看到這些檔名時,就知道事情沒有這麼簡單:
整個遊戲居然都是React
做的!
子彈是React
元件,場景切換是路由切換,狀態管理用的Redux-Saga
。
不得不說,這個專案寫下來,Redux-Saga
這套狀態管理方案作者肯定是玩兒明白了。
專案間通訊
為了將B站彈幕抓取專案中識別的指令實時傳遞給坦克大戰,需要使用websocket
協議。
這裡我選擇的是socket.io
庫。
值得一提的是:需要在服務端(也就是彈幕抓取專案)的socket.io
配置中設定cors: true
解決跨域問題。
在坦克大戰中,接收到的指令構造成鍵盤事件派發出去:
const fireKeyEvent = (evtType: string, keyChar: string) => {
var KeyboardEventInit = {key: keyChar, code: keyChar, location:0, repeat:false, isComposing:false};
var evtObj = new KeyboardEvent(evtType, KeyboardEventInit);
document.dispatchEvent(evtObj);
}
總結
整個過程沒有什麼技術難點。唯一比較坑的是:直播有5秒左右延時,所以從彈幕發出到操作坦克有延遲。
在5秒延遲的情況下,本來弱智的電腦,簡直天神下凡。
為了減少玩家的挫敗感,我決定,讓玩家互相對決。
這樣,大家都在同一起跑線上啦。
最後,在一片彈幕中,渡過了一個祥和的中秋節夜晚。
我不是說直播間的各位水友,我說我自己,真夠無聊的......
歡迎加入人類高質量前端框架研究群,帶飛