如何用B站彈幕控制遊戲

卡頌發表於2021-09-26

大家好,我卡頌。

中秋節在家無聊,想整點兒好玩的。思來想去決定做個用彈幕控制的坦克大戰

具體邏輯是:

  1. 監聽直播間水友們的彈幕
  2. 將彈幕中有效的指令提取出來
  3. 將指令轉化為鍵盤按鍵在坦克大戰中輸入
  4. 直播坦克大戰遊戲畫面

這樣就實現從操作展示的完整邏輯,所有直播間的水友都可以參與遊戲,下面是實際效果:

讓我們來看看技術細節吧。

監聽水友們的彈幕

這一步我使用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秒延遲的情況下,本來弱智的電腦,簡直天神下凡。

為了減少玩家的挫敗感,我決定,讓玩家互相對決。

這樣,大家都在同一起跑線上啦。

最後,在一片彈幕中,渡過了一個祥和的中秋節夜晚。

我不是說直播間的各位水友,我說我自己,真夠無聊的......

歡迎加入人類高質量前端框架研究群,帶飛

相關文章