🎯

昨年のリベンジ!UI/UXをReactで刷新した文化祭ダーツシステムを開発した話

に公開

概要

この記事では、先日開催された文化祭のダーツ出店で使用するために開発した、投影用スコアボードアプリケーション「Darts Score Board」の技術的な裏側をご紹介します。

昨年度のシステム(別記事参照)がUI/UXの点で課題があったため、今年はReact (Vite) といったモダンな技術スタックでフルリニューアル。スコア入力といった基本機能に加え、スコアに応じたガチャ演出プレミアム機能トップ3入賞時の祝福演出など、会場を盛り上げるための機能を多数実装しました。

作ったシステム

はじめに

今年も文化祭が無事終了しました。私達はダーツ屋を出店したのですが、その運営には自作のスコアボードシステムを使用しました。

実は昨年も同様のシステムを開発したのですが、UI/UXの面で評判が芳しくなく、運営オペレーションにも支障が出ることがありました。そこで今年は「モダンな技術でリベンジしよう!」と決意し、ReactとViteを採用。UI/UXを根本から見直すだけでなく、来場者にもっと楽しんでもらえるような「演出」にこだわったシステムの開発に挑戦しました。

去年と今年のシステムの違い

昨年のシステムについては、こちらの記事で詳細をご確認いただけます。
https://zenn.dev/kinnkinn/articles/b4af9a32e45889

ダーツ屋の運営フロー(開発要件)

今回のシステム開発は、実際の店舗運営フローをそのままデジタル化し、体験を向上させることが目的でした。当日の運営フローは以下の通りです。

  1. 受付・プラン紹介: お客様に2つのプランを紹介します。
    • スタンダードプラン (200円)
    • プレミアムプラン (300円)
  2. 支払い・投擲: お支払い後、お客様はダーツ5本を受け取り、投げます。
  3. スコア確認: 刺さった矢を(お客様またはスタッフが)写真に撮ります。
  4. スコア入力: PCの前に移動し、本システムを使って「名前」と「プラン(スタンダード/プレミアム)」を選択し、写真を元にスコアを入力します。
  5. ガチャ演出: 確定ボタンを押すと、スコアとプランに応じてA賞、B賞、S賞のいずれかの演出動画が再生されます。
  6. 景品交換: お客様は表示された賞(A, B, S)のエリアから好きな景品を一つ選んで終了です。

主な特徴

開発したスコアボードには、主に以下の特徴があります。

  • 🎮 直感的なスコア入力: ダーツボードのUIをクリックするだけで簡単に入力できます。
  • 🎰 スコア連動のガチャ演出: スコア確定時、点数に応じた3段階の動画が再生され、ゲーム性を高めます。
  • 👑 プレミアム機能: 豪華なUIのトグルスイッチで「プレミアムモード」に切り替え可能。ガチャの判定が優遇されます。
  • 🏆 トップ3祝福演出: トップ3にランクインすると、ファンファーレと共に祝福アニメーションが自動再生されます。
  • 🔊 効果音による体験強化: スコア追加時、プレミアム切り替え時など、随所に効果音を配置し臨場感を高めました。

技術選定の理由

今回の開発では、昨年の反省を踏まえ、以下の技術スタックを採用しました。

役割 利用技術 選定理由
ライブラリ React 18 プレイヤーリスト、スコア、モーダルの表示など、UIの状態が複雑に変化するため、宣言的なUIとコンポーネントベースの管理が最適でした。
ビルドツール Vite 開発サーバーの起動が非常に高速で、HMR(ホットリロード)も快適。文化祭までの短期間での開発効率を最大化できました。
スタイリング CSS Modules コンポーネントごとにスタイルを閉じ込めることができ、グローバルなCSS汚染や命名規則の衝突を気にせず開発に集中できました。
データ永続化 localStorage サーバーレスでの運用が絶対条件でした。文化祭中にブラウザを閉じたりPCが再起動したりしても、データが消えないようにする最も簡単な方法でした。
ホスティング Vercel Git連携でデプロイが瞬時に完了します。後述する「当日の緊急修正」を、運営を止めずに反映させるために不可欠でした。

システムアーキテクチャ

システムはReactのコンポーネントベースで設計されています。メインのAppコンポーネントが全プレイヤーの状態(players)を管理し、localStorageと同期させています。

App (メインコンテナ・状態管理)
├── Leaderboard (スコアボード・入力フォーム)
│   └── EditPlayerModal (プレイヤー編集モーダル)
├── DartArea (ダーツボード表示・位置調整)
├── GachaModal (ガチャ演出用モーダル)
└── CelebrationOverlay (祝福演出用オーバーレイ)

データフローはシンプルです。

  1. Leaderboardでユーザーがスコアを入力。
  2. AppコンポーネントのStateが更新される。
  3. 更新されたStateがlocalStorageに保存される。
  4. Stateの更新をトリガーにGachaModalCelebrationOverlayが表示される。

開発のハイライトと苦労した点

こだわった演出機能と、その実装で直面した課題です。

1. ガチャ・祝福システムのロジック

会場を盛り上げるための中核機能が「ガチャシステム」です。スコアと、プレミアムかどうか(premium)の状態で再生する動画を動的に切り替えています。

▼ 非プレミアムユーザー

スコア範囲 表示動画
< 90 b.mp4 (ハズレ演出)
90-139 a.mp4 (アタリ演出)
≥ 140 s.mp4 (激アツ演出)

▼ プレミアムユーザー

スコア範囲 表示動画
< 120 a.mp4 (アタリ演出)
≥ 120 s.mp4 (激アツ演出)

プレミアムユーザーは、低いスコアでも「アタリ演出」以上が保証される仕組みです。
さらに、ガチャ演出後にトップ3にランクインしたかを判定し、該当者にはファンファーレ(fanfare.mp3)と共に祝福オーバーレイを表示させます。

2. ブラウザの自動再生ポリシーとの戦い

ガチャ演出には動画と音声が不可欠ですが、現代のブラウザは「ユーザーの操作なしに音声付きメディアを自動再生する」ことを厳しく制限しています。

当初、スコア追加ボタンのクリックをトリガーにvideo.play()を実行していましたが、非同期処理などを挟むとブラウザに「ユーザー操作起因ではない」と判断され、再生がブロックされる問題に直面しました。

そこで、以下のようなフォールバック処理を実装しました。

  1. まず、async/awaitで音声付きの再生(videoRef.current.play())を試みます。
  2. 失敗した場合(catch節)、ブラウザにブロックされたと判断します。
  3. videoRef.current.muted = trueに設定し、ミュート(無音)状態で動画を再生します。
  4. 同時に「🔊 音を有効にする」ボタンを表示し、ユーザーが明示的にクリックすることでミュートを解除できるようにしました。

この対策により、いかなる状況でも最低限の動画演出は保証しつつ、ユーザーが望めば音声も楽しめるUXを実現できました。

3. 運営を考慮したプレミアム機能のUX

プレミアム機能は、豪華なアニメーション付きのトグルスイッチで実装しました。

このスイッチをONにすると専用の効果音(premium.mp3)が鳴るだけでなく、文化祭当日のスムーズな運営も考慮しています。

プレイヤーのスコア追加が完了し、次のプレイヤーの入力に移る際には、スイッチが自動的にスタンダード(OFF)に戻るようにしました。これにより、スタッフが手動でスイッチを戻す手間(戻し忘れ)を省き、スムーズなオペレーションを実現しました。

4. 当日の緊急デバッグと機能追加

どれだけ事前にテストをしても、本番では予期せぬアクシデントや仕様変更が起こるものです。今回も当日にいくつかの緊急対応が発生しました。

Vercelにデプロイしていたおかげで、修正はローカルで行い、GitHubにプッシュするだけ。運営スタッフには「ブラウザをリロードしてください」と伝えるだけで、システムを止めることなく即座にアップデートを反映できました。

以下が、当日に急遽変更・追加した内容です。

  • ガチャの点数ボーダー変更: 「思ったより高得点が出る(出ない)」といった当日の状況を見て、ガチャ演出(a.mp4やs.mp4)が切り替わるスコアのしきい値を急遽変更しました。
  • ダーツボードの配色修正: システムのUI(白黒)が、実際に使用している物理ダーツボードの配色と異なっていることが判明。プレイヤーが混乱しないよう、CSSを修正し、物理ボードと同じ配色(セグメントの色)に即時変更しました。
  • ボードへの点数表示: UI上のダーツボードをクリックした際、どの点数エリアを選んでいるか分かりにくいというフィードバックを受け、クリックしたエリアの点数が即座に表示されるよう機能を追加しました。

当日の操作方法(オペレーションマニュアル)

文化祭当日にスタッフ向けに用意したマニュアルの抜粋です。

1. 初回設定(プロジェクターとの位置調整)

初めて起動した際や、プロジェクターの位置がズレた際は、画面左上の設定アイコンを押して調整バーを表示します。
背景のダーツボード(物理)と、画面上のダーツボード(デジタル)がぴったり重なるよう、「横位置・縦位置・大きさ」をスライダーで調整してください。調整後は再度アイコンを押してバーを隠します。

2. 基本操作(スコア入力)

  1. プレイヤーの名前(ニックネーム)を入力します。
  2. ダーツが刺さった場所を、画面上のダーツボードをクリックして入力します(3回まで)。
  3. (もし該当する場合)プレミアムスイッチをONにします。
  4. スコアを追加 ボタンをクリックします。

3. 演出の確認

スコアを追加すると、ガチャ動画が再生されます。トップ3に入賞した場合は、続けて祝福演出が表示されます。
⚠️ 重要:演出再生中は、次の入力を行わずお待ちください。

4. 修正・取り消し

▼ 確定前の修正
ボードのクリック場所を間違えた場合は、Undo(1つ前に戻る)やReset(そのラウンドの入力をリセット)で修正できます。

▼ 確定後の修正
スコアを追加を押した後に間違いに気づいた場合は、リーダーボード上の修正したいプレイヤーの名前部分をクリック(またはEnter)してください。スコアや名前を修正する専用画面が表示されます。

5. ⚠️ 注意事項(データリセット)

画面右下のスコアリセットおよび新しいゲームボタンは、すべてのゲーム記録を消去してしまいます。運営中は誤って押さないよう、十分に注意してください。

手元で動かしてみる

このシステムはVercelにデプロイしているほか、ご自身のPC(ローカル環境)でも簡単に実行できます。

前提条件

  • Node.js (LTS版を推奨)
  • npm (Node.jsに同梱)
  • Git

セットアップ手順

ターミナル(コマンドプロンプトやPowerShell)で以下のコマンドを順番に実行してください。

# 1. リポジトリをクローン(ダウンロード)
git clone https://github.com/kinn00kinn/darts-score-board2.git

# 2. プロジェクトフォルダに移動
cd darts-score-board2

# 3. 必要なパッケージをインストール
npm install

# 4. 開発サーバーを起動
npm run dev

起動に成功すると、ターミナルにLocal: http://localhost:5173/のようなURLが表示されます。このURLをブラウザで開くと、スコアボードが起動します。

おわりに

昨年の反省を活かし、Reactをはじめとするモダンな技術スタックを採用したことで、UI/UXを大幅に改善できただけでなく、リッチな演出も実装でき、非常に満足のいく開発体験となりました。

文化祭当日も、ガチャ演出や祝福演出のたびに来場者から歓声が上がり、会場の盛り上がりに大きく貢献できたと実感しています。

この記事が、Reactを使ったイベント用アプリケーション開発に興味を持つ誰かの参考になれば幸いです。
もしよろしければ、GitHubリポジトリにスターをいただけると大変励みになります!

Discussion