本日は「ボードゲーム・パズルプログラミング Advent Calendar 2022 - Adventarアドベントカレンダーへのエントリーです。

asobannについて

新型コロナの影響でワークショップなどに人が集まれないようになってきた2020年から、オンラインのボードゲームサービスhttps://games.yattom.jp/asobannasobann(遊盤)を開発しています。

asobannはボードゲームを遊ぶ環境として、一種の物理エンジン的なアプローチをしています。つまり、ゲーム固有の動きやルールを実装するのではなく、汎用的なカードを並べたり、手札にしたり、ボードを置いてそこにコマを自由に配置したりでき、逆にそれしかできません。同じ頃に活用を始めた、オンラインホワイトボードのMiroなどにも影響を受けています。

ボードゲームを遊ぶだけでなく、自作ゲームを実験したり人に遊んでもらう場としての活用も考えています。自分でも半分教育・半分遊びのゲームを作っており、心理的安全性ゲームボードゲーム『チームで勝て!』なども、asobannでオンライン化しました。『チームで勝て!』はasobann上で開発、テストを主にしています。

asobann上の心理的安全性ゲーム

アーキテクチャと技術スタック

asobannが一番大事にしているのは、アナログゲームを人と一緒に遊んでいる感覚の再現です。カードは1枚1枚、ドラッグで引いてきて手札に並べます。手番を回すときは声を掛け合います(コミュニケーション機能はないので、Zoomなどの併用が必須です)。他の人がカードを選ぶとき、指先が迷っている様子が見えます。

そうしたリアルタイム性のため、Socket.IOを採用してプレイヤー間で状態を共有しています。状態はAWS ECS上にMongoDBを立て、そこで一元管理しつつ、各クライアント(ブラウザ)には動きの差分のみを送信して通信量と処理量を節約しています。

ゲームのコンポーネント(カード、ボード、カウンターなど)はシンプルですが、それでもドラッグする、裏返す、シャッフル、トレイに載せるなどの挙動の実装はそれなりに手間もかかり、複雑化します。テーブル全体の状態をクライアント間で矛盾なく共有するため、状態は操作元のクライアントで変更し、差分をサーバー経由で他クライアントにプッシュし、同時にサーバーが完全な状態をマスターとして保有しておいてクライアントがリフレッシュできるようにしました。おそらく対戦ゲームなどのノウハウが世の中にはたくさんあると思うのですが、手探りで設計、実装しました。

サーバーはPython / Flaskです。サーバーの実装は、ほぼクライアントからの差分を他クライアントに転送し、状態全体を更新・取得するだけの薄っぺらいものになっています。

クライアント(ブラウザ)はJavaScriptで、RE:DOMというごくシンプルなDOM操作フレームワークを使っています。ユーザーの操作(ローカルでもリモートでも)をDOMに反映するのには、独自のフレームワーク的なものをアプリ内で育てながら使っています。コンポーネントをDOMにするのか、それともCanvasで描画するほうがいいのか、あまり考えられておらず、もしかすると自前でCanvasにせっせと描画するほうが、安定するのかなという気もしています。

インフラは自分の勉強のつもりでAWS ECSとAWS CloudFormationを採用して、いちから調べながら実装しました。このへんの顛末は以前に当ブログに書いています

アプリの実装やインフラのコードはGitHubにあります。

github.com

困っていること・これからやりたいこと

まずはパフォーマンスの問題があって、人数が多かったりゲームが複雑(コンポーネントが多い)だったりすると、動きが緩慢になったり同期がおかしくなったりしています。Webソケットが詰まっている気がするのですが、理由があまりつかめていないので再現と原因調査が必要です。再現するための自動テストがなかなか面倒です。以前にも挑戦したのですがいまいち再現できませんでした。

ゲームを一緒にやっている感じを出すための機能も追加したいと思っています。emoteを打てるとか、相手にちょっかいを出せるとか、全体を回転できる(各自自分の正面でテーブルを囲むように見える)とか。

またゲーム開発をasobann上でするための機能を作り込んでいきたいところです。今は手元でJSONを作ってアップロードするだけですが、テーブル上でコンポーネントを編集できるようにしたいなあと考えています。