この記事は、CAMPHOR- Advent Calendar 2021の21日目の記事です。
Ruby on Rails に hotwire-rails という gem を導入すると、ブラウザ・サーバー間の双方向通信がかんたんに実装できます。
それを利用して、ある種のブラウザオンラインゲームを簡単に実装にできるという話と、その一例として、オンラインで対戦できるリバーシを作った話をします。
作ったもの
オンライン対戦できるリバーシを作りました。
ソースコードはこちら:GitHub - genya0407/reversible
この Web アプリでは、盤面の状態はサーバーで管理されています。そして、サーバー上で盤面の状態が更新されるたびに、その変更がブラウザに反映されます。例えば、自分が石を打つと、サーバー上の状態が更新され、即座に対戦相手の画面も更新されます。これを実現するために、JavaScript は一行も書いていません。すべてのロジックは Ruby と テンプレートエンジン、CSSのみで記述されています*1。
私のような、JavaScript をまともに書けないサーバーエンジニアでも、Hotwire を使えばブラウザオンラインゲームを雑に作ることはできるようでした。もっとも、アクション性の高いものを作るのは難しそうですが。
Hotwire とは
Hotwire について詳しく知りたい方は、公式サイトがよくまとまっているので、これを読むのをオススメします。
非フロントエンドエンジニアがゲームを作るという文脈において重要なのは、サーバー上のイベント起因でブラウザの状態を更新するという操作を、 JavaScript を一切書かずに実現できるということです。
Hotwire でオンラインゲームを実装する方法
Hotwire では、オンラインゲームを以下のように実現できます。
- 参加者がゲームのページを開き、WebSocket のコネクションが確立される
- hotwire-rails が提供するヘルパーを使うと1行で書けます
-
reversible/show.html.slim at trunk · genya0407/reversible · GitHub
- 参加者が自身の行動を表すHTTPリクエストをサーバーに送信する
- 例えば 「(1, 5) の位置に黒石を置く」のようなPOST リクエストを送信する*2
-
reversible/_board.html.slim at trunk · genya0407/reversible · GitHub
- サーバーは、サーバー上の盤面の状態を更新する
- 盤面の状態は ActiveRecord や Redis で管理するのが良いでしょう
-
reversible/moves_controller.rb at trunk · genya0407/reversible · GitHub
- サーバーは、更新後の状態を元にHTML文字列を生成し、参加者全員の WebSocket コネクションに対して送信する
- 参加者のブラウザは、WebSocket 経由でHTML文字列を受け取り、それを画面に描画する
- hotwire-rails が提供する JavaScript のライブラリがいい感じに処理してくれます
つまり、ゲームの状態はすべてサーバーが管理し、それを元にHTMLを生成する処理もサーバー側で行います。ブラウザはそれを画面に描画し、操作するためのインターフェースに特化します。
一般に、サーバーもフロントも自分で作るのにわざわざ JSON API を定義するのはダルいと言われています*3。Hotwire なら、ロジックをサーバーに集約し、密に結合した一つの世界で、ブラウザを含めたすべてをコントロールすることができます。JSON API のこちらとあちらで同じロジックを再実装する必要はもうありません。
まとめ
Hotwire を導入することで、普通の Ruby on Rails アプリに双方向通信を容易に持ち込むことができます。それを利用し、ブラウザで遊べるオンラインゲームを気軽に作れることがわかりました。
もう少し頑張れば、将棋やチェス、麻雀、カタンのような、より複雑なボードゲームを作ることもできると思います。Hotwire でゲームを作るのは非常に新鮮で楽しいです。皆さんもぜひ体験してみてください!
*1:hotwire-rails が提供するJavaScriptライブラリは動いています。ここではあくまで「私が」JavaScript を書いていない、ということを主張しています。
*2:aタグをクリックしてPOSTが飛ぶのはちょっとトリッキーですが、Rails の仕組みを使って実現してます Rails学習者にrails-ujsの動作説明したら感動された話 - INODEVLOG
*3:要出典