2020-09-22

API Gateway + WebSocket なオンライン対戦ゲームを作った

Twitter で #web1week という お題に沿ったサービスを1週間で作成、公開してみよう!という趣旨のハッシュタグがある。

9/7 ~ 9/13 に「2」というお題で開催されていて、友人に誘われて参加することにした。

どうせだから新しい技術に触れつつ何か作りたいなーと思い、 前から気になっていた Golang と WebSocket を使って簡単なオンライン対戦ゲームを作ることにした。

どんなアプリ?

相手よりも早く答えが2になる数式を作ることを目指す、一対一のオンライン対戦アプリ。 ソースコードはこちら

screenshotゲーム画面のイメージ。

システム構成

awsシステム構成図。

API Gateway + Lambda で WebSocket 接続を受け付ける。 対戦相手のマッチングには SQS、部屋情報とユーザー情報の管理には DynamoDB を用いている。

処理の流れ

部屋は以下の状態遷移をする。

  • WAITING: 対戦相手のマッチング中の状態
  • PREPARING: 問題文をユーザーに通知し、その応答を待っている状態
  • PLAYING: 対戦中の状態

処理の大まかな流れは以下の通り。

  • クライアントから join にリクエスト(new WebSocket()

    • SQS を確認してタスクがない = マッチング中の対戦相手がいない場合は、状態が WAITING の部屋を新規作成して、現在のユーザを部屋に追加し SQS にタスクを登録。
    • SQS を確認してタスクがある = マッチング中の対戦相手がいる場合は、部屋にユーザーを追加して状態を PREPARING に変更。
  • クライアントから problem にリクエスト(WebSocket の onopen ハンドラ内)

    • 部屋の状態が PREPARING の場合は、部屋に所属するユーザーに問題文を通知して、部屋の状態を PLAYING に変更。
    • 部屋の状態が PREPARING 以外の場合は、マッチング中なので待つ旨を通知。
  • クライアントは問題文を受け取り次第、画面に表示。

  • ユーザーが回答ボタンを押下したとき、回答内容を solve にリクエスト

    • 正解の場合、勝敗結果を部屋に所属するユーザーに通知
    • 不正解の場合、その旨をリクエストユーザーに通知
  • クライアントは勝敗結果を受け取り次第、画面に表示。leave にリクエストして接続断。

詰まったところと解決策

当初は DB から 状態が WAITING の任意の一部屋を検索し、 マッチングしようとしたが、DynamoDB ではレコードの全件取得をしないと無理そうだったので断念。 代わりに SQS を利用することに。

が後に、SQS の場合、ユーザーがマッチングを待たず離脱して接続断が発生した場合などに、 SQS に残ったゴミをケアする必要があることに気づいた。 エラーにすることで対応したが、UX 的にはあまり良くなさそう。

感想

  • Go 書くの楽しい!スクリプト言語とコンパイル言語のいいとこ取り感がある。 リンタなどのツールが公式で用意されているのも嬉しい。モジュールの扱いなど一通り触れてよかった。

  • DynamoDB の使いどころが難しい。検索が絡むと途端に DynamoDB だけで解決するのが困難になりそう。 個人開発だとお金もかかるから別に DB とかは使いたくないけれど、何か良い設計パターンがあるのだろうか。

  • マッチング系アプリの UX 設計の難しさ。待ちや通知のタイミングをこだわりだすと時間がどんどん溶ける。 今回は時間も限られていたから適当に決めたけれど、一度きちんと向き合って設計してみたい。

© 2019 uu