enebularのwebsocketノードで対戦ゲームを作ってみる

#一人用鬼ごっこを通信対戦ゲームにしたい
この記事は[enebular Advent Calendar 2021](https://qiita.com/advent-calendar/2021/enebular)の23日目の記事なります。

以前、p5jsを使った一人用のかくれんぼゲームを作ってみました。
上下左右操作＋Aキー/Fキー操作で、移動しながらプレイヤーの色を変えられ、背景の色になじむことができます。
なじんでいる間は鬼は見失い、なじまなくなったら全力で追いかけてくる鬼ごっこです。

https://twitter.com/canonno_blog/status/1293361014549823490

https://qiita.com/canonno/items/00738c7d928c3ec655d5

この時は鬼はロジックで組まれたコンピュータだったのですが、**どうにか通信対戦できないか**、と考えwebsocketの勉強を始めました。
そんな中、enebularにwebsocketのノードがあること知り、これを使えばサーバーサイドはノーコードで実装できるのでは・・・？と思いトライしてみました。

是非是非ご覧ください！

#完成デモ
アクセスした直後にplayer or oniと聞かれるので、逃げる側の場合はplayer、鬼側の場合はoniと記入します。
左側のブラウザ操作 (逃げる側)の動きが、多少のラグを伴って右側のブラウザ (鬼側)に伝わっているのが分かりますね。
これができるなら結構いろんなゲームが作れそう。

https://twitter.com/canonno_blog/status/1470723800740564992

#実装こまごま
##そもそもwebsocketとは？
そもそもwebsocketって何じゃらという話はこの記事が分かりやすかったので是非ご覧ください。
ザクっと、一方向のHTTPと違って、双方向通信を実現してくれるのがwebsocket。
通信対戦ゲームを作るときによく使うプロトコルみたいです。

https://qiita.com/chihiro/items/9d280704c6eff8603389

##クライアント側の実装
実装は[このサイト](https://www.1ft-seabass.jp/memo/2020/05/26/vue-node-red-collaboration-using-websocket/)を参考にしました。
今回は標準のWebsocketライブラリを使ってみました。
Websocketの部分のコードはこんな感じです。
Websocketの接続先はデプロイしたenebularの情報からインプットし、逃げる側は/nigeru、鬼側は/oniに接続するように設定しました。

```js
      var ws = null;
      
      //playerかoniか選んでもらう
      var player_or_oni = window.prompt("player or oni")
      var ws_url = "ws://(enebularから情報を取ってくる).herokuapp.com/ws/"

      //それぞれパスを付け足す
      if (player_or_oni =="player"){
          ws_url += "nigeru";
      }else{
          ws_url += "oni";
      }

      // ベースのURL＋WebSocketのパスでつなげる
      ws = new WebSocket(ws_url);
      // WebSocket が接続された open イベント設定
      ws.onopen = hanlderWebSocketOpen;
      // WebSocket からメッセージを受け取った message イベント設定
      ws.onmessage = hanlderWebSocketMessage;

      // WebSocket が接続された open イベント
      function hanlderWebSocketOpen(event) {
          ws.send('Hello! Node-RED!');
      }

      // WebSocket からメッセージを受け取った message イベント
      function hanlderWebSocketMessage (event) {
        var res = JSON.parse(event.data)
        console.log("中身は：",res["p_or_o"] )
      }

      async function  hanlderSendMessage(_player_or_oni) {
        //自分で操作した場合は情報を送る
        if (_player_or_oni == player_or_oni){
          // JSON データは JSON.stringify で文字列にしてから送っています
          var _message = { content: message, p_or_o : player_or_oni };
          await this.ws.send(JSON.stringify(_message));
        }
    }
```
この基本を押さえたうえで、通信のイメージはこんな感じ。
一方のプレイヤーが操作した情報を、enebular経由で他方のプレイヤーに伝えるスキームを考えました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/8ddc198c-b3df-cdb9-4d42-cb067f32f242.png)

##enebular上のフロー
フローはこんな感じ。
素直に送られてきた情報をもう一方のノードに渡しているだけになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/6e4898c4-896f-8e10-e4df-977700cad531.png)

websocket inノードの設定はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/b8d2adf3-1ebc-3432-a17f-8a695af97f0e.png)
えんぴつマーククリック後はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/634d94ee-1470-d688-93b2-3638d4fa9f00.png)

functionノードはこんな感じ。
ここが一つのハマリポイントで、このmsg._sessionの削除をしないと、セッション保持の都合で相手に情報が伝達できなくなってしまいます。
これ知るまでに何時間もハマってしまった・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/8793e75d-eb7a-2b0a-ead9-320de1818607.png)

jsonノードはこんな感じ。
特にトリッキーなことはしてません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/c64b2a84-d601-abf9-b269-5a6f280a3e01.png)

websocket outノードはこんな感じ。
websocket inとほぼ同じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/a93a0327-717a-2a3d-6b57-502cfda13513.png)
えんぴつマーククリック後はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/1b6fc43d-44ef-24ca-cd02-185859c37ffc.png)
ちなみにfunctionノードが必要になるのはwebsocket outの仕様みたいです。
一応こんな説明書きがありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/e14e13cc-52c9-6a5c-44ad-15b971722eee.png)

##WebsocketのURL
enebularでフローを作った後にデプロイし、画面右上のインフォマークにマウスオーバーします。
httpsのURLが出てきますので、ここをwsに書き換えてクライアント側に実装すればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666990/c0753887-90a2-1a1b-d4ec-a8ea32a23514.png)

#最後に
今回はenebularのwebsocketノードを使って対戦ゲームを実装してみました。
サーバーサイド側がシンプルなので、クライアント側の実装に集中できそうですね。enebularすごい。


今回の実装だと、部屋分けをして「特定の誰か」と「特定の誰か」で戦う、みたいな要件が実現できません。
部屋分けを実現する場合別のライブラリが必要になりそう。

https://qiita.com/k_g/items/dd25b3d4d7101cf8c446

おいおい勉強してみようと思います！