1ヶ月半のチーム開発でオンライン対戦ができるボンバー●ン風なゲーム 「ボムボムパニック」を作ったので、構成や工夫したことなどを書きます。
ゲームはこちらから
コードはこちら
機能の紹介
トップページ
ロビー
対戦 / CPU
壁が落ちてくる演出
結果発表
システム構成
種類 | 使用技術 |
---|---|
フロントエンド | TypeScript |
バックエンド | Node.js / TypeScript |
ゲームエンジン | Phaser3 / matter.js |
リアルタイム通信 | Colyseus |
デプロイ(フロントエンド) | Vercel |
デプロイ(バックエンド) | Cloud Run |
CI / CD | GitHub Actions / Cloud Build |
ビルド | Vite |
マルチプレイのゲームを作るということでバックエンドが必須だったため、フロントエンドと共通して開発できる TypeScript & Node.js を選定しました。
フロントエンドはゲームを描画するため、Phaser3
というゲームエンジンを採用し、サーバサイドでも利用可能な物理エンジンである Matter.js
を合わせて利用しています。
通信は Phaser3
とのサンプルが豊富で WebSocket ベースで動く Colyseus
を採用しました。
実行基盤は、Ping Test の結果、一番 latency の低くなりつつ無料で使用できる Google Cloud Run
を選択しています。(フロントエンドはなんでも良かったが日本リージョンがある Vercel
を使いました)
裏側の仕組み
通信を同期する方法
マルチプレイゲームを実現する方法はいくつかありますが、今回は以下の図における右側を実装しました。
これは以下の思惑によるものです。
- クライアント側を信頼せず全てサーバ側で管理したい
- ホストが切断するとゲームが終了するのを避けたい
- キャラクターの移動、HP、ステータス
- ブロックの破壊
- アイテムの出現
- 爆弾の設置、爆発
- 爆風、誘爆の処理
- 時間
など全てサーバ側で管理し、クライアント側は描画のみを行う形としています。
爆弾の同期について
プレイヤーの移動はシンプルで、送られてくるキー入力を使ってキャラクターを移動して、結果をクライアントに伝えるだけです。 しかし爆弾の場合はそうはいきません。
爆弾を設置する場合の通信フローはこのようになっており、ローカルAから爆弾設置リクエストが飛んでくるとサーバは全てのクライアントに通知します。(実態はサーバに配置した上で、描画のためだけにクライアント側にも配置するイメージ)
この時、各クライアントとサーバ間は異なる latency で動作をしているため、クライアントがレスポンスを受け取ったタイミングで爆弾を設置すると、クライアント間で異なる状況になってしまう可能性があります。(ローカルAでは爆弾が描画されるが、ローカルBでは描画されない)
そこで各クライアントとサーバ間で同じ時刻を共有した上で、以下の図のように 「いつ爆発させるのか?」 を含めた情報を同時に送り、クライアントの画面で爆発するタイミングを同期できるようにしました。 ※あまりにも latency が大きいクライアントの場合は正常に動かない場合があります
誘爆について
爆弾の爆風の範囲に別の爆弾があると 誘爆 という現象が起きます。 これが結構厄介で苦労したポイントでした。
何が厄介かというと単体の爆弾だけであれば先ほどのやり方で問題なく同期ができるのですが、誘爆の場合は下の図のように爆弾が爆発する直前に、クライアントから爆弾設置のリクエストが送られてくると、(爆風の範囲内であれば)まだクライアントに同期していない爆弾も爆発してしまいます。
そこで各クライアントとサーバ間で「いつ爆発させるのか?」だけではなく 「いつ爆弾を設置するのか?」 を含めた情報を同時に送り、クライアントの画面に出現するタイミングと爆発するタイミングを両方同期するようにしました。
これにより、爆弾を設置するタイミングを latency を考慮して調整することで、確実にクライアントに爆弾設置の通信が届いているだろう時間にサーバとクライアントで同時にボムを設置して処理を行うことで 同じ誘爆結果 を表現できるようにしました。
また、爆風は画面上のアイテムやブロックの状態によっても形が変わってしまうため、アイテムとブロックの設置や削除についても爆弾と同様に、サーバとクライアント間でタイミングを合わせて生成・削除するようにしました。
敵AIについて
ゲーム AI を作ったことなど全くなかったので手探りで始めました。 非常に参考になった(というより考え方をそのまま適用した)のがこの記事です。
この記事は鬼ごっこのようなゲームを例に挙げ、アイテムを拾いながら鬼から逃げるAIの作り方 を教えてくれます。具体的な手法としては 「影響度マップ」 を作ろうというもので、アイテムからの距離や鬼との距離などを計算して、全てのマス目に対して評価値を計算し、一番評価値の高いところを常に目指すように動く AI です。
引用: ゲームAI -基礎編- 『知識表現と影響マップ』 | Cygames Engineers' Blog
今回は以下の要素を評価値として AI を作りました。
- 現在地からの距離
- アイテムとの距離
- 将来爆風が発生する可能性があるかどうか
- 爆弾を設置するのに適した場所かどうか
- 他のプレイヤーとの距離
また、結構アホな動きをすることが多かったので、逃げるルートがなくなるタイミングでは爆弾を設置しないなどの処理を入れています。
※PathFinding
(From から To までの最短経路を探す) を使っている
実際に動いてる AI がこちら。(noname 以外 AI です)
他にも苦労した点
- プレイヤーの移動をサーバを介してもスムーズに見せるところ
- 可能な限り原作を忠実に再現しようとしたところ
- 爆弾を置いたときは、爆弾の上を移動できるが、一度離れると衝突判定が出てくるとこ
- 時刻の同期
- アセットの用意
- そもそもの技術選定
感想
作る前は荒唐無稽な感じで始まったチーム開発でしたが 1ヶ月半という短い期間で機能てんこ盛りでちゃんと動くオンラインゲームが作れて感無量です。
人間やればできるんだなと思うと同時に、考えればアイデアが湧くというのを実感した期間でもありました。 一緒に開発してくれた皆さんどうもありがとうございました!!!