🃏

忘年会の余興で自作のクイズアプリを開発した話【フロントエンド/デザイン】

に公開

はじめに

これはなに

会社の忘年会の余興として、オリジナルのクイズアプリ「オンリーワンゲーム」を企画し、開発まで担当しました!

本記事では、新入社員である私が、ゲームの企画からフロントエンド実装、UI・デザインに至るまでの制作過程や、試行錯誤の裏側について記録しています。

自己紹介

忘年会幹事・余興担当を任された新入社員です。

  • 本プロジェクトの発起人&リーダー。
  • 日本の市町村ヲタク。全市町村の出身の人に出会うのが人生の目標。現在229市町村/1741(=13.15%)
  • 普段はデジタルツイン技術の研究開発をしている。

開発メンバー

  • (リーダー)
    • ゲーム企画とフロントエンドとUI&デザインを主に担当
  • 会社の同期S君
    • バックエンド/インフラ担当。タスクをお願いすると次の日には仕上がったものを渡してくれます。きっと彼の記事に開発の苦労などが語られるでしょう。
  • 会社の同期Aさん
    • 優秀なデザイナー。デザインを担当してもらう予定だったが、本業が多忙のため、Figjamの運用やデザイン、企画の相談に乗ってもらいました。

使用技術スタック

  • フロントエンド:React
    • 半ば勉強目的です。本PJと同時並行の案件でも使用していたため、勉強モチベで選定しました。
  • バックエンド:FastAPI, SQLAlchemyなど

背景

当初、アプリを作ることになるとは思っていませんでした。

私の会社では、新入社員は6月に本配属が決まった直後、部署からの洗礼として半年先の忘年会の幹事業務を担当する”しきたり”がありました。私はなんとなく面白そうだった余興係に手を挙げ、余興をすることになったのでした。

余興の内容は自由でした。考えているうちに、Youtuberのクイズ企画やボードゲームによくある「回答が被ったらアウト」系のゲームが頭に浮かび、これを200人でやったら被りまくってめっちゃ面白いんじゃね?と思い至りました。(実際に考え始めて10分くらいで思いつきました。)

しかしこのゲームができる、既存のゲームアプリやサービスはどこにもありませんでした。

「ないなら作ればいいじゃない!」

8月に入り、私が「この人と一緒にアプリ開発したい!」と思った同期のS君とAさんに声を掛けてみると、彼らはすんなりと承諾してくれました。(神)

そうしてメンバーが揃い、「オンリーワンゲーム」のアプリ開発は始まりました。

オンリーワンゲームとは

ゲームの発案

約200人が同時に参加するという珍しい状況で行うゲームとして、私は次の3つの要素を満たすようなゲームにしたいと考えました。

  • 運があると勝てる、知識があると有利
  • 参加者のプレイが他の参加者のプレイに影響する(相互作用)
  • 新入社員の名前を知ってもらう

私は、2つ目の相互作用を最も重要な要素だと思っています。例えば普通のクイズをすると、他の参加者のプレイに関係なく正解し続ければ勝つだけの単調なものになってしまいます。

また、マルチプレイゲーマーからすると、自分のプレイが他のプレイヤーに影響するというのは、謎の大きな欲求を満たせるのです!

これらの要件を満たすよう、次のルールを設定しました。

ルール

記述回答のクイズ形式のゲームで、ルールは次の通りです。

  • 答えが複数あるお題が出され、参加者は他の参加者と被らないような回答を1つ提出する。
    • お題の例:都道府県
  • 全員の回答が集計された結果で、同じ回答をした人数が少ないほど高得点をゲットできる。
    • n問目でk人被りの回答をした人の点数:(100+20n)/kの小数切り上げ
      • 例:
        • 0問目 単独正解:100pt, 2人被り:50pt, 3人被り:34pt, ...
        • 1問目 単独正解:120pt, 2人被り:60pt, 3人被り:40pt, ...
        • 2問目 単独正解:140pt, ...
  • 新入社員と同じ回答をするとボーナス得点をゲットできる。
    • 単独正解の1/4のptを加算。
  • お題は第0問~第5問の全6問。
  • 回答時間は60秒くらい。基本的に運営が参加者の回答状況を見て調整する。
    • 回答に間に合わなかった場合はそのお題の得点は0点となる。

総合得点の上位5名には景品をプレゼントしました!

ゲーム画面

ここまでで、おおよそどんなゲームか見えてきたと思います。

さっそく、私の成果物であるゲーム画面のご紹介とその詳細を、ゲームの流れに沿って見ていきます!

入室画面

入室画面
会場に配られたQRコードを参加者が読み取り、Webブラウザ上で最初に見る画面です。
名前とチームを選択して入室します。
チームは部署名5つと、その他のjokerチームを選ぶことができます。

私が名前の入力欄に文字数制限をつけることを失念していたため、当日に40文字くらいある記号だらけのやばい名前で入室してきた人がいたことは秘密です。

また、あえて「所属」や「部署」ではなく「チーム」としました。これは、当日の参加者の中に派遣、出向、退職、転職、役員などの色々な状況の方々がいて、今の所属・部署の括りにできないのです。
この課題は数ヶ月引きづっていたのですが、「別に所属って正確じゃなくてよくね?」「チーム名を部署にして、みんな好きに入ってもらえばよくね?」という私の柔軟な発想により完全解決しました。

チームでの表彰もないため、チームそのものを無くすという選択肢もありました。
ですが、ゲーマーはチーム戦に熱量が沸く生き物なので、私がどうしてもチームの概念は残したいという我儘で粘り、なんとか形になりました!

  • ちなみにチームの表彰をしなかったのは、チーム(部署)間での人数差が大きかったからです。

デザインの話をすると、最初に決まったのはこの奇抜な背景でした。
今回作るのはツールではなくゲームなので、楽しげな背景にしたくてこれに決めました。他のパーツはこの背景に合うように載せていく、という順番で組み立てました。

背景の候補(Pinterestからいただきました)↓
背景の候補

「苗字」「名前」の入力フォームと「チーム」のプルダウンだけMUIを使いましたが、ここ以外は全てCSSで自作しました。(非効率)
このほかのボタン類は、デザインの参考になりそうなサイトをたくさん観察して、見よう見まねでなんとか作りました。

出題待機画面

出題待機画面
入室した後に訪れる画面です。

このゲームでは、運営の指示するタイミングで一斉に画面進行を行います。この画面は参加者全員が同時に出題画面に移動するための”スタートライン”の役割を担っています。

参加者がボタンを押すことで擬似的に進行を同期させています。
サーバーサイドから同期処理する技術と余力は我々にはありませんでした...

ですので、運営の出題合図前に下の「出題画面へ」ボタンを押しても次の画像のようなモーダルが出てきて、進めないようになっています。

運営の出題コントロールについては後ほど!

待機モーダル

参加者にはせっかちな人がたくさんいて、こういうボタンは必ず連打してきます。この「出題画面へ」ボタンを1回押すと1回APIが叩かれる仕様なので、S君が「ここのボタンみんなに連打されたらサーバー落ちるかも...」と不安そうにしていました。
そこで、待機モーダルの表示/非表示のアニメーションをわざとゆっくりにし、2秒以上の間隔を空けないとボタンを押せないようにしました。アニメーションを使うだけで回避できるコスパの良い方法でした!

先に入室した人向けに、ルールを載せました。
我々運営が前でスライドを使ってルール説明をするのとは別に、あらかじめ参加者にルールを一通り読んでもらうことで、ルールをより早く理解し定着してもらうためです。

そして、「このゲーム何がおもろいねん」とお考えの方に向けて、画面下部で「こうやって楽しむんだよ」と伝えてあげています。これは私がいつもボドゲを友達にルール説明する時にやる手法です。

画面上部に目をやるとヘッダーがあり、これ以降最後までずっと表示されます。
ここで自分のアイコンと現在の順位と合計得点を見れます。

アイコンの色はチームごとに決まっていて、こちらの配色決めは会社のデザイン部門の同期Aさんにご協力いただきました。

彩度とか操っていて強かったです。
30色のカラーパレットを作ってくれて、そこから一緒に6つ選びました。バイキングみたいで面白かったです。
カラーパレット

アイコンのデザインは、初期案として下の画像のような「フルネームを円の中に2段で詰め込む」「苗字、名前を1文字ずつとって2文字を大きく並べる」など考えていました。
見やすさを考えた結果、最終的に「フルネームを円の中に1段で詰め込む、ただし、5文字以上の場合は苗字名前が2文字ずつになるように後ろから削る、また苗字と名前が1文字の場合は残す」というなんとも複雑なロジックになりました...

アイコン案

下の具体例を見てください。

  • 田中/隆 → 田中/隆
  • 田中/良太 → 田中/良太
  • 田中/良太郎 → 田中/良太 (1つ上と区別つかない)
  • 長谷川/太郎 → 長谷/太郎
  • 滝/良太郎 → 滝/良太郎
  • 滝/ひろゆき → 滝/ひろゆ
  • はせがわ/隆 → はせが/隆

複雑な仕様ですが、このためだけに参加者にプロフィール画像を登録してもらうのも実装が大変すぎるので、この方法に落ち着きました。

将来的には、デフォルトはこれにして、画像登録できる機能も追加したいですね。

出題画面

出題画面

前画面の「出題画面へ」を押すことでこの文字通り出題画面にやってきます。ゲーム中で2番目によく見る画面です。

参加者はお題を見て、被らなさそうな渾身の回答をします。ですが回答は選択式ではなく、あえて記述式にしています。というのも、選択式にしてしまうとただのくじ引きになってしまうからです。知識、ひらめき、運を大事にしたかったので、あえて記述式にしています。

画面では、上段にお題が何で、有効回答数が何個なのか、中段に回答例と注意を表示しています。
下段の回答欄に入力して矢印ボタンを押すと、入力した回答がこちらの用意した回答に含まれているかチェックされます。s
チェックが通ったら提出ボタンが出現し、その回答でよければボタンを押して本提出します。

出題画面遷移

この画面はUI的に非常にこだわって作りました。参加者が一番「ゲームする」のはここですからね!

上の画像を左からご覧ください。入力欄をタップすると、左から2枚目の画像のようにお題と回答例のフレームが簡易的に圧縮されて入力欄とともにヌルヌルっと上へ移動し、下に来るキーボードのスペースを確保しています。
このとき別のところをタップすると、キーボードが消え、圧縮されたフレームが展開されてヌルヌルっと元に戻ります。触ってて楽しいです。

回答を入力し(3枚目)、矢印ボタンを押して回答チェックをパスすると、右矢印がヌルッと回転して下矢印になり、色も水色からグレーに変わります。そして提出ボタンがスッと降臨します(4枚目)。この演出により、矢印ボタンのお役御免を漂わせて提出ボタンに注意を誘導します。初見だと矢印ボタンが何なのか説明がないため、この演出でそれとなく説明しているわけです。

5枚目のように間違った回答を入力して回答をチェックすると、警告が出て、再入力を促されます。

回答チェックの処理は単純で、例えば都道府県のお題のときは、あらかじめ北海道から沖縄県までの47データを正解リストとしてもっておきます。そして入力された回答が正解リストの中に入っているか調べて、yes/noを返すだけです。

回答チェックはクライアントサイドで行っています。

結果待機画面

結果待機画面
参加者が回答提出した後、運営が集計完了の操作をするまで待機してもらう画面です。

この画面、他と比べてだいぶ殺風景ですよね...ここで参加者を楽しませる何かができないかメンバーと議論したのですが、ゲーム性と関係ない部分のため開発を後回しにして、放置されたまま気づいたら忘年会当日を迎えました。(ポケモンとかでもこういう未実装要素あるもんね。)

とはいえ、出題時間は約60秒で短い上に、当日はS君と私でその間ずっと喋り倒すことで参加者はきっと退屈しないだろう、と考え、実装コスト0のこの方針になったのでした。

出題待機画面と同じく、運営の合図の前に「結果を見る」のボタンを押すと、せっかちな人を制止するためにアニメーション付きのモーダルが表示されます。
結果待機画面モーダル

回答結果画面

回答結果画面
全参加者の回答を集計した結果を表示する画面です。ゲーム中で一番盛り上がる、一番大事な画面です。そのため機能が盛り盛りになり、実装が一番大変でした。

ここでは自分の回答の結果と、他の参加者の回答がどうだったのかを見ることができます。

上段の自分の回答の結果として、順位、自分同じ回答をした(被った)人、得点を見ることができます。この青いフレームをタップすると、被った人のリストがモーダルで表示されます。(上の画像の右側)

このモーダル、地味に実装が大変でした。アイコンの並びを1行5列にしていて、アイコンの数が5の倍数を超えるごとにモーダルをいい感じに上下に長くする、という実装をしていました。Copilot万歳。

新入社員と回答が被ったときのボーナス得点を強調するため、アイコンは黄色く輝く演出が施されています。

中下段は全体の回答結果として、被った人数が少ない順(正確には得点順)に各回答を見ることができます。
初期表示では、得点上位3つと下位3つの回答を表示していて、「結果詳細」ボタンで全回答の一覧をモーダルで見ることができます。一覧の最上部は誰も回答しなかった0人回答、そこから下へ得点の高い順で続いていき、スクロールして見ることができます。↓

回答リスト

この画像のリストは、忘年会当日に想定される参加人数200人がランダムに回答したときの分布になっています。
また、自分が回答した「香川県」は水色で表示されています。

本記事の最後のおまけデータ集に、当日のデータを記載しています。
このシミュレーション結果と、当日のオンリーワンの回答人数、最も被った回答の人数を比べてみてください。

各回答をタップすると、自分の回答をと同様、被った人のリストがモーダルで表示されます。
また、回答リストの右部にオンリーワンな回答をした人のアイコンが表示されます。オンリーワンは素晴らしいことなので、特別感を演出しました。

ここで私が本当はやりたかった実装として、1人(オンリーワン)の回答項目の位置が画面上部端にくるようにスクロール位置を初期化したかったのです。が、完全にコケました。いくら調べてもスクロール位置を初期化する方法がありませんでした。Copilotの敗北

上の画像でいうと、和歌山県の欄の上部が画面上端に来て欲しい。

最後に、実際にこのゲームをプレイした人も気づいていなさそうな隠れ機能を紹介します。
アイコンを押し込むと、その参加者のフルネームと所属が書かれたツールチップが現れます。
アイコンは名前が4文字までしか表示されず、また文字が小さいため、ツールチップにして参加者情報を拡大して見れるようにしました。

そして最後まで謎だったのが、ローカルPC環境だとこのツールチップが動作してくれないのです。最初はローカルで動いてくれていたのに、途中からデプロイしてスマホで見ないと動作検証できないという開発者泣かせの機能と化したのでした。なんで...?

ブラウザの検証ツールがうまくいってないだけの説はあります。
さらに、もうAWSを止めてしまったので、これだけ説明させておいてツールチップのスクショ画像もないというこの有様...

運営が次の出題を開始したら、水色の右矢印をタップして出題画面に戻ります。

最終結果閲覧画面

最終結果閲覧画面
全6問が終了したら、この画面に飛びます。

回答結果画面と仕様はほぼ同じです。
違うところとして、各お題の全参加者の回答結果を切り替えながら見ることができます。

当日は、参加者がこの最終結果を見ている時間を使って、会場の前では我々運営がせかせかと景品や表彰の準備をしていました。

参加者のスマホに映る画面の紹介は以上です。

個人ランキング画面

得点TOP5
ゲームが終了し、表彰のときに会場のスクリーンに映すものです。

グラフの色はアイコンと同じでチームの色になっています。
グラフの長さは、1位の得点を右端、5位の得点を画面中間に固定して、2位〜4位は1,5位の得点の比率と合うような長さに調整されるように実装しました。
得点の表示位置は、グラフの長さが70%を下回るとグラフ右部に表示されるようにしました。図形の内側にある名前と得点の文字が重なるのを避けるためです。

またこの画面と次のチーム別平均得点ランキング画面の背景色がこれまでと異っていますが、これは本番で会場のスクリーンに映しているスライドの背景に合わせています。ん...?なんでそんなややこしいことしてるの?と思うかもしれませんが、詳細は後述します。

チーム別平均得点ランキング画面

チーム別平均得点ランキング

個人ランキング画面のチーム版です。画面仕様もとほとんど同じです。

チーム別の平均得点の計算処理として、1問でも回答が間に合わず0点となった人は集計対象外としています。これは0点の人がチームの足を引っ張ってしまうから、、ではありません。

このゲームの仕様上、入室画面で誰でもどんなチーム(自分と関係ないチーム)としてでもアカウントを量産することができてしまいます。例えば、ある参加者が10個アカウントを作り、そのうち1個でゲームをプレイし、残り9個を放置すると、残り9個のアカウントの総合得点は全問時間切れにより0点となります。そのため、この9個のアカウントの所属チームの平均点がおかしくならないようにする必要があり、このような仕様にしています。

運営のためのイベント進行管理画面

イベント進行管理画面
我々運営が当日、ゲームの進行を管理するための画面です。S君が内部API含め全部作ってくれました!

各問題の出題、提出締切&集計結果などの進行をこれらのボタン操作でコントロールします。
内部的には、データベースに進行のstatusの値を用意していて、開始・集計ボタンを押すとAPIが叩かれstatusの値が更新されます。
実は、参加者が出題待機画面、回答結果画面などで押すボタンは、APIを叩いてここのstatusの値を参照し、値によってクライアント側で次の画面に進むか決めていたのでした!

開発プロセス・きもち

ここでは開発・作業の流れと、そのときの苦労などを順を追って見ていきます。

アプリ開発開始まで

背景パートで話しましたので簡単に。

6月に配属されて早速、半年先の忘年会の係を決めることになりました。一番楽しそうだったので、自分から余興係で手を挙げました。ついでに「しおり係」にもなりました。1つ上の代の先輩社員が、他の係は結構大変です!といった話をした一方、余興・しおりはまあまあ大変なくらいです、と仰っていたので、私もうまく口車に乗せられたものです。

時は過ぎ、8月下旬。同期全員で余興の内容を決めるミーティングがあり、その日私が電車で移動中にパッと思いついたのがこのゲームでした。
同期全員から様々な余興案がありましたが、このゲームを提案してみたら皆このゲームをすんなりと受け入れてもらえました。

そして私は優秀そうな(乗ってくれそうな)S君に声をかけ、開発が始まるのでした。

開発スケジュール

まず全体観としてFigjamでスケジュールを作成しました。といっても未経験すぎて右も左も分からないため、S君がChatGPTにそれっぽい開発スケジュールを出力させました。

開発スケジュール
(本番終了後にスクショしたため各箇所で完了した感じになってます。)

計画から本番まで3ヵ月強のスケジュールでした。フェーズで分けると、

  • 企画:1ヵ月
  • 設計:2週間
  • デザイン:2週間
  • 実装:1ヵ月弱
  • 不具合修正、機能追加、負荷テスト、お題選定など:1ヵ月弱

でした。次からそれぞれ個別に見ていきます。

企画:1Month

ゲーム内容がおおよそ決まったところで、休日に同期のおうちに同期9人で集まってオンリーワンゲームのテストプレイをしました。
LINEで9人のグループを作って、お題を決めて、せーので全員一斉に投稿する原始的な方法で再現しました。

このとき9人で試したお題は、

  • 日本の球団名【12球団】
  • 関東の都県【7】
  • 都道府県【47】
  • 東京23区【23】
  • 100以下の素数【25】
  • 日本の祝日【17】
  • 世界四大文明【4】
  • 料理のさしすせそ【5】
  • 太陽系の惑星(冥王星あり)【9】

でした。参加人数と選択肢の数のバランス、選択肢の知名度の偏り具合によるゲーム性や、ゲーム中の参加者の気持ちを検証できました。

ここでの検証でわかったことは、

  • 選択肢について
    • 参加人数に対して選択肢が多すぎるとき、被らないのが当たり前で、被るとかなり悲しくなる。
    • 参加人数に対して選択肢が少なすぎるとき、被るのが当たり前で、被らないとかなり嬉しいが運ゲーすぎる。
    • 選択肢の知名度に偏りがあると、マイナーな選択肢を知ってたときの有利感が楽しい。あえてメジャーな選択肢を攻める戦略性が増す。
  • ゲーム中の参加者の気持ちについて
    • 結果を見るときが一番ドキドキする。
    • 被ってないと嬉しいし、自分の渾身の回答が被ってても面白い。
    • 自分の回答が誰と被ったのか気になる。
    • どの回答が一番被ったのか、被らなかったのか気になって見ちゃう。
    • 「誰」がどの回答をしたのかは、知り合いでもあまり気にならない

ということでした。大体想像通りですが、一番最後は私にとって意外な結果でした。

これらを踏まえてお題づくりを進め、また画面構成に優先順位をつけて設計していきます。

設計:2Weeks

まずは余興でアプリ開発した直近の代の開発者3名の方にコンタクトを取り、「余興でこんなゲームを作りたい!」という話をするためミーティングをしました。6つ上くらいの代らしく、余興でアプリ開発するのは弊社としても久々のようでした。
社会人として初めて自分でミーティングを設定し、初めての顔も知らない先輩方とのミーティングで緊張もしましたが、今後の進め方などを丁寧に優しく教えてくださったので本当にありがたかったです。

作りたいゲームの概要を説明した後、
私「まず何から取り組めば良いでしょうか?」
先輩「マジレスすると業務フロー図じゃね?」

業務フロー図作成

ということで業務フロー図を作りました。

業務フロー図

Aさんが慣れた手つきでFigjamを運用してくれたので、綺麗に書けました!
業務フロー図を作ることで、具体的なゲームの流れ、必要な画面要素・データを洗い出してメンバー間で整理できました。また、ゲームの成立に最低限必要な要素を整理し、実装順も決めました。

データ/API設計

データ設計

データベースに持たせる属性を洗い出し、整理しました。

S君と話し合いながらも、意外と考えることが多く、すんなりと決まりませんでした。
特に、userの回答内容(content)とscoreはどこのテーブルに持つといいのか、に悩みました。

最初は、Userテーブルにquestion0からquestion5の属性を用意してそこにcontentとscoreを格納する、という方法も考えました。しかし問題数の拡張性を考えると、上図のようにAnswerテーブルに持たせるのがベターと判断しました。その結果、運営が回答集計ボタンを押下すると、scoreとtotal_scoreを求めるためまずAnswerテーブルをupdateし、その直後にUserテーブルをupdateするという仕様になりました。

このあたり何がベストだったのか全くわからない。動けばそれでいい

Progressテーブルについては、運営のためのイベント進行管理画面で触れたのでご参照ください。

デザイン:2Weeks

Figmaの画像

デザインは私がFigmaを使ってゴリゴリ描き上げました。画面詳細については前述にあるためここでは省略します。

ちょうど当時から1ヵ月前に、社内の研修でFigmaを使ったアプリ開発を行っていたため、コンポーネント、バリアント、オートレイアウトなどの機能を学習コストなしで使えたため、良い速度感で開発を進めることができました。

デザインするときに意識したのは、テストプレイで得たユーザー体験(UX)です。

ゲーム中の参加者の気持ちについて

  • 結果を見るときが一番ドキドキする。
  • 被ってないと嬉しいし、自分の渾身の回答が被ってても面白い。
  • 自分の回答が誰と被ったのか気になる。
  • どの回答が一番被ったのか、被らなかったのか気になって見ちゃう。
  • 「誰」がどの回答をしたのかは、知り合いでもあまり気にならない

回答結果画面を見返していただくと、これらを意識した画面になっていることが分かると思います。
過大な情報量もUX向上に繋がらないことと、一度に見せる画面の大きさに制約があることを逆手にとっていい感じに落とし込めました。

実装:1Month

Figmaで画面デザインが描き終わり、先輩社員の方からも高評価をいただいたので、いよいよ実装です。

初心者なので、とにかく画面要素をコンポーネントに区切って、再利用性と拡張性を優先して実装しました。可読性?保守性?ロバスト性?なんですかそれ、知らないコトバですね。

うまくいったところとして、Figmaの画面設計によって各要素がピクセル単位ですべて決まっていたため、大きさ、座標、曲半径、RGBカラーなどに魔法の数字を入れていくだけでFigmaそっくりの綺麗な画面が出来上がっていくのはとても良い体験でした。

しかし、設計段階では気づけず、実装してみて初めてぶつかった壁もありました。

途中入退室の処理がめんどくさすぎる

設計までは、ゲームの最初から最後の結果発表まで全参加者が入室している前提で進めていました。
しかし実際は、食べ飲み放題の会場で200人全員が通しでゲームに参加することはなく、途中トイレのため会場を出る人、忘年会自体に遅れてやってくる人、通信環境やアルコールによりゲーム進行に置いて行かれる人もいるでしょう。これらの人々のために、途中入退室の仕様を決め、実装することとなりました。

まずこのゲームの入室処理として、入力された苗字・名前・チームがUserデータベースに存在しない場合は参加者が新規作成されます。すでにデータベースに存在する場合は、その参加者として再入室できます。これを使って再入室処理を詰めていきます。

ちなみこれを悪用すると、ここでも述べたように、一人が複数参加者を作成したり、ある人が他人の苗字、名前、チームで入室して好き勝手回答するなど、暴れることもできちゃいます。(S君と議論の末、どうしたらいいか分からず未対応のまま)

ここで面倒くさかったのは、途中入室・退室、進行に置いて行かれる、を各画面遷移について考えなければならず、その遷移動線が多かったことです。

  • n問目の出題中/結果待機中/結果閲覧中に初入室
  • n問目の出題中/結果待機中/結果閲覧中に再入室
  • n問目の出題中に画面を放置し提出せず、ゲーム進行がm問目の出題/結果閲覧のときに回答を遅れて提出
  • n問目の結果閲覧中に放置し、ゲーム進行がm問目の出題/結果閲覧のときに遷移ボタンを押下

上の動線がメインの要対応部分で、他にも画面はあるためもっと動線はあります...これらの動線を実装しながらチェックするのが面倒でした。この進行管理周り、何かライブラリがあるのかもしれませんが、私の学習/導入コストと天秤にかけて、気合いとパワーで乗り切ることにしました。

最終的な仕様として、運営からの回答締め切りに間に合わなかった回答は、遅れて提出しても無回答扱いとし、各画面の次に進むボタンは、その時のゲーム進行に沿った画面に直接ジャンプするようにしました。こうして、途中退室や置いて行かれるケース、途中初入室、再入室を区別せずにうまくカバーすることができました。

お題の選定

お題が決められない

開発から離れ、ゲーム企画の話になります。
実はこのゲームで一番苦労したといっても過言ではないのは、お題の選定です。

このゲームはお題に対して回答チェックを行うため、次の条件を満たすお題であることが必要です。

  • 選択肢がお題に当てはまるかどうか、明確に誰もが同じ線引きができること
  • 選択肢の表記揺れがほぼ無いこと。

そして厄介なことに、これらの要件と選択肢の知名度のバランスをいい感じに満たすお題がなかなか思いつかないのです。

このゲーム性の既存のサービスが存在しないのも、これがネックになっていると私は思っています。

最初はお題として、「都道府県」「世界の国」を考えていました。都道府県は「現在のもの」、世界の国は「日本が現在認めているもの」と条件をつければ、全員にとって選択肢の線引きが明確になりますし、知名度のバランスも十分です。

逆にすぐ思いつきそうな「赤いもの」「動物」のようなお題は選択肢が無数にあり、線引きのために条件を指定することが難しいのです。

実装しながら並行して私・S君・Aさんと数ヶ月お題を考えていたのですが全く思いつかず、同期を集めてミーティングまでわざわざ開いてもらいました。
そこで捻り出したお題が、

  • 税(所得税、固定資産税、入湯税など)
    • 堅いお題なのと、我々よりたくさん納税している先輩社員に、楽しい忘年会でこの話題を出すのは...と考え却下。
    • パッと思いつく税がそんなになく、回答のバリエーションが少なそう。
  • 弊社の部署名
    • 頻繁に名前が変わったり統廃合するため、OBの方や転職した方に対して不適切と考え却下。
  • プログラミング言語
    • 線引きが怪しく、こちらも正解リストが作りにくいので却下。

と、数は出たものの、結局いいお題には巡り合えませんでした。

そしてお題の件を放っておいて実装を進めていると、不具合の修正も含めて先に実装が完了してしまいました。
忘年会本番まであと6日、お題だけが決まっていない状態でした。

「数が50個くらいある、ゲームになりそうなお題を教えて」
などとMicrosoft Copilotに訊き続けていると、ふと神の発想が私の脳に直撃しました。

Copilotに50個選択肢を決めてもらって、それをそのまま答えにしちゃえばいいじゃん

お題決め革命

例えば、「Copilotが決めたペット50選」というお題にすれば、誰かが「〇〇がないぞ!」と文句言ってきても「Copilotが決めたことが全てです」で通せばよいのです。

Copilotを噛ませることで、好きな選択肢の数に調節でき、また馴染みやすいお題を量産でき、まさに革命が起こりました!「Copilotが決めた赤いモノ50選」のように、本当になんでもよくなったのです!

生成AIが答えそうな選択肢を狙うという別のゲーム性が生まれましたが、それはそれで面白いので良しとしました。
応用として、生成AIでなくても「〇〇さんが決めたXX50選」でも良いですね!

適切な選択肢の数

選択肢の数が自由になったので、参加人数200人に対して何個がベストなのかをS君に検証してもらいました。

200人がランダムな回答を数万問繰り返すシミュレーションを行った結果、40~50個くらいの選択肢にすると5人前後がオンリーワン回答、最も被った回答の人数が10人前後となり、私が求める理想的な分布になることがわかりました。

シミュレーションは、S君がPythonですぐ作ってくれました!
本記事の最後の「おまけのデータ集」に当日のデータがありますので、理想の分布と実際の分布を比べてみてください。

本番採用されたお題

  • Q0 都道府県
  • Q1 2024パリオリンピック・パラリンピックの種目
    • NHKのあるホームページにあった40種目。
    • 2024年の忘年会にふさわしい、良い時事問題でした。
  • Q2 Copilotが思う、おススメのプログラミング言語40選
    • 最初プログラミング言語は泣く泣く却下していたので、IT企業の弊社として嬉しい復活です。
    • 生成AIが発達した年だったので、これも時事問題として見せることができました。
  • Q3 Copilotがペットにしたい動物50選
    • AIが発達しすぎて自我を持ち始めた設定で、あくまで「AIが飼いたい動物」というテイです。
    • 大喜利っぽかったので、このとき見せたスライドは黄色と黒のIPPONグランプリ風に「Copilotがペットにしたい動物、どんなの?」としました。あまりウケませんでした(なんで?)
  • Q4 ひらがな
    • 濁点半濁点なし「あ」~「ん」の48択。
    • 完全な運ゲーを私がどうしてもしたかったので入れました。回答の分布気になりますよね。社会実験にできそうです。
    • 当日のMCで「そろそろ頭使うの疲れたでしょう?」を枕詞にしました。
  • Q5 都道府県リベンジ
    • 「このゲーム性が分かって来たところでリベンジです」が枕詞。
    • 最初のお題のリベンジは、意外にも会場のウケが良かったです。

お題決定後、事前に私を含めた同期10人に各お題の回答を聞いて、データベースに手動でセットしてバックエンドは準備完了です。

当日は幹事の業務で余興に参加する余裕がないためです。

忘年会当日

お題も決まり、当日を迎えました。

余興が始まる10分前に、ゲームアプリのQRコードを印刷した紙を会場の各円卓に置き、参加者に入室とルール説明画面まで進んでもらいました。

余興が始まり、私とS君は会場の大きなスクリーンの前に立ち、ルール説明やお題のスライドを映しながら、9割私が喋って1割S君が合いの手を入れる形式で進めました。

話す内容は前日にS君と綿密に打ち合わせしました。時間を測りながら2回通し練習もしました。

出題中の時間に話す内容と、各お題で結果が出た時に、MCとしてどこに触れてどんなコメントをすると良いのか、をきっちり事前に決めていました。

最初の0問目の運命の結果発表、開発者としてバグらないか本当に不安でした。結果的に、0問目だけ2名ほどバグってましたが1問目以降は問題ありませんでした!動くって素晴らしい!

おそらくトランザクション系のバグだと思っています。

結果発表では、ガッツポーズして喜んでいる人、うわあ...と悔しがっている人、叫ぶ人、色々いて、私はゲームの進行をしつつ参加者のリアクションを見て楽しんでいました。ゲーム開発者冥利に尽きる瞬間でした。

ゲームが終わり表彰の時間に移るとき、ここでもひと工夫していました。

会場のスクリーンに映すお題などが書かれたスライドはPower Pointで作ったもので、ゲーム内容とは連動していません。本当は連動させたかったのですが、開発コスト的に断念しました。そのため、表彰のスライドに書くべき表彰者の名前は、結果発表直前まで空欄のままなのです。

そこで、最終結果閲覧画面に進行したとき、私があえて参加者にゆっくり結果を見せる時間を作り、その間にS君にスライドへ表彰者の名前をせかせかと手入力してもらっていました!

忘年会前日に、名前を素早くスライドに手入力をする練習もしてもらいました(笑)

また、手作業だと表現しにくい最後の2つのランキング画面はゲームと連動しており、これを結果集計後にS君がスクショしてスライドに貼ることで、会場のスクリーンに映し出していました。

このようにS君のマンパワーによって、スライドの内容がゲームに連動しているように見せる工夫をしていたのでした。

ランキング画面だけ背景画像が違ったのは、そのスクショをスライドに貼り付ける際に、スライドの背景と同じ画像にして違和感をなくしたかったから、というわけでした!

忘年会を終えて

参加者からの声

35分間の余興が終わりました。やりきってS君と拳を交わしたのを覚えています。

気になる参加者の反応は...とにかく大好評でした!何人もの先輩方から「素晴らしかった」「毎年これでいい」「アプリとして売れる」などと言っていただけました。嬉しい!

この余興は1次会で行っていたのですが、私が印象的だったのは2次会でも先輩方が「x問目のときのこの回答が被ると思わなくて~」などと余興のゲーム内容の話で盛り上がっていたことです。我々がアプリ開発したことではなく、ゲームの内容で盛り上がってくれたのがゲーム企画者として本当に嬉しいものでした。

機材や景品授与においても、トラブルなくスムーズに進められました。

トラブったら200人でじゃんけん大会をするつもりでした。本気でその覚悟でした。

ここまで触れていませんでしたが、景品も私が考えていました。
個人優勝の景品として「弊社カラーのNintendo Switchです!」のくだりは会場で拍手が起き、しっかりウケました。

もっと良くするには?

Github Copilotがもっと早くあったら

開発で死ぬほどお世話になったMicrosoft Copilotという相棒がいたのですが、彼には毎回VSCodeからソースコードを貼り付けて質問していたので、かなり面倒でした。テキストフィールドも縦幅が小さいので書きにくいし...

忘年会の前日の夜、もう開発も実装も終わっているこのタイミングで、なんとGithub Copilotが使えるようになりました!
こいつはVSCodeに組み込めて、複数ファイルを自動で読み込んでくれる優れものなのです。なんでやねん!

もっと早くこいつを使えていればあんなに苦労しなくて良かったのに...と悲しい気持ちになりました。(相棒とは)

実装方法をもっと良くするには

今回の開発における実装は、私が初心者すぎたのと、迫りくる期限に追われて「動けばOK」の精神で実装していました。

ある程度勉強してきた今振り返ると、基本的なところでも、きっと使わなくていいuseEffectとか、きっと使った方が良いuseRefとかメモ化とかがたくさんあります。データの持ち方、機能のメソッドへの切り方など、まだまだだなと感じます。

デザインにおいても、今回はCSS職人になってましたが、マテリアルデザインに基づいてMUI(Box,Stackとか)をもっと多用しても良かったなと思います。

今後このアプリをアセット化することがあれば、(大規模になりますが)この辺りを意識してリファクタしたいと思います!

このゲームをもっと良くするには

一部は前に述べましたが、このゲームアプリの改善点・課題として、次のものがあります。

  • 参加者が他人のアカウントに入れないようにしたい
    • 初入室時に簡易的なパスワードを設定する処理を追加するなど
  • ゲーム進行を同期処理にして、運営の進行で参加者の画面を自動で遷移させたい
    • 途中入室や画面を放置したときに効果的
  • 結果待機画面が殺風景
    • 画面にハートボタンを押して、参加者がボタンを押したら全員の画面にハートを飛ばす演出とか(Aさんのアイデア)
  • ボタンで画面遷移をするのではなく、横方向スワイプを使った直感的な操作で実現したい
  • アイコンを画像登録できるようにしたい

このゲームをアプリ化するときは、これらの課題をクリアしたいです。

振り返りと学び

振り返り

これまでの自分の開発経験を振り返ると、「自分が作ったアプリをその場で大勢の人に使ってもらい、評価を受ける」ことを人生で初めて経験することができました。簡単には得られない貴重な経験であると確信しています。

自分で使って満足しちゃってクオリティもぼちぼち、なんてアプリがたくさんあるのはあるあるですよね。

またチーム・プロジェクトという視点で振り返ると、かなり上手くいったと思っています。

  • チームビルディング、ファシリテーション
    • チーム内のコミュニケーションはとても円滑で、楽しく取り組めました。おかげで建設的な議論もたくさんできました。
  • メンバーへのタスク分割
    • ダメなリーダーとして言われる、他人のタスクを手伝っちゃう!ではなく、お互い開発範囲を決めて丸投げしたのが良かったです。
  • スケジューリング
    • 全体的にやや早いくらいの開発速度でした。忘年会1ヵ月前からはのんびり作業していた感覚でした。
    • 機能・必要なタスクに優先順位を付けたこと、できないことは潔く諦めたことが功を奏したと思います。

    これができたのも、相互レビューもなければ好き勝手コーディングして進捗を進めていたから、という側面もあります。

学び

技術的なところでは、とにかくReactとjavaScriptの基本的な構文に慣れることができました。
hookでapiを叩いて渡すことすら初めてだったので、こういった基本的な実装をこの開発で学びました。

また開発速度においては、Figmaの正確な設計や、ドキュメントによってメンバー全員が同じ成果物を想像できることの重要性を学びました。

チームの熱量の維持も大事ですね。私はずっとアツアツでしたが、S君はどうだったのでしょうか。私からすると最後まで熱量をもってついてきてくれたと思っています。

このアプリの将来

このゲームのボトルネックはなんといっても問題制作の難易度です。生成AIがあるとはいえ、遠くないところに限度があると思っています。

そのため、このゲームを少し抽象化した感じのゲームがたくさん遊べるアプリで、その中にこのオンリーワンゲームがある、という方が汎用性が高いのかなとなんとなく考えています。

需要が限定的とはいえ、このゲームは会社の忘年会の余興ソリューションとしては最高だと思うので、問題設定をできるようにして、各々の会社に合う問題で遊んでもらえるプロダクトにしたいです。

おわりに

自分を含め、開発メンバーのみなさん(n=2)、本当にお疲れさまでした。おかげさまで今自分ができる最高の余興ができました。

忘年会が終わって半年近く経ってからこの記事を書いているというのに、これだけ詳細に書くことができたのは、私にとってこのゲームの開発経験が鮮烈で、色々なものを得たからだと思います。

私にゲームアプリ開発の時間をくださった弊社の方々、余興の参加者の皆さま、そして開発メンバーのS君とAさん、本当にありがとうございました!

【みんな気になる】おまけのデータ集

各問題のオンリーワン回答と最も被った回答

Q0:都道府県

回答タイプ 回答 ひとこと
オンリーワン回答 青森県, 福島県, 新潟県, 長野県, 静岡県, 大阪府, 岡山県, 鳥取県, 沖縄県 多種多様でびっくり
最も被った回答 神奈川県(10人) 忘年会の会場があるからでしょうか...
誰もしていない回答 宮城県, 愛知県, 広島県, 福岡県 地方中枢都市を避けがち

Q1:2024パリオリンピック・パラリンピックの種目

回答タイプ 回答 ひとこと
オンリーワン回答 シッティングバレーボール, レスリング, ホッケー, ウェイトリフティング, ラグビー, ローイング, 車いすテニス, 車いすフェンシング ローイングよく出たな...
最も被った回答 ブレイキン(11人) 新種目として話題でしたね
誰もしていない回答 自転車, 車いすバスケットボール, 車いすラグビー, ゴールボール ゴールボールって知ってます?

Q2:Copilotが思う、おススメのプログラミング言語40選

回答タイプ 回答 ひとこと
オンリーワン回答 Lua, Clojure, PL/SQL PL/SQLを一言一句合わせた人凄い
最も被った回答 COBOL(15人) 参加者の年齢層を感じます...
誰もしていない回答 Dart, Objective-Cなど(多いので一部抜粋) 書き方で合わせるのが難しそう

Q3:Copilotがペットにしたい動物50選

回答タイプ 回答 ひとこと
オンリーワン回答 ヤドカリ, グッピー, レッサーパンダ, リス, カエル, ハト, コイ, コアラ コアラは飼ってみたい
最も被った回答 イグアナ(10人) 一番被ったのこれ...?
誰もしていない回答 ハヤブサ, ヤギ, ヒツジ, シマリス(多いので一部抜粋) ヤギ・ヒツジは出そうなのに...

Q4:ひらがな

回答タイプ 回答 ひとこと
オンリーワン回答 く, せ, め, ら, ろ, わ, を, ん 「わをん」答えた人強い!
最も被った回答 ひ(9人) いろんな理由が考察できそう
誰もしていない回答 お, る 忘れられてそうなのと、避けられすぎてそう

Q5:都道府県リベンジ

回答タイプ 回答 ひとこと
オンリーワン回答 新潟県, 静岡県, 山梨県 新潟と静岡が再びオンリーワン!
最も被った回答 北海道, 福岡県(8人) 福岡は0問目では誰も回答していない
誰もしていない回答 奈良県 本当に忘れられてそう...

設定した特別賞

  • ベストカップル賞
    • もっとも同じ回答をした回数が多いペアを表彰。
    • 今回は3回同じ回答をしたペアを表彰しました。
  • 1年目のことよく分かってるで賞
    • 新入社員の回答と同じ回答をした回数が多い人を表彰。
  • 最も被ったで賞(最下位)
    • 6回とも時間内に回答した人の中で総合得点が最下位の人を表彰。

Discussion