Wii-Fi サーフィン

https://github.com/wasijyayo/Megaro_wave

TypeScript

React

JavaScript

Node.js

Firestore

wiibord を使ってwifiの波に乗りポーズを決めろ!

わしじゃよ

伊勢エビ

カニ食ったサバ

推しアイデア

まるでサーファーが波を探すようにWifiという波を探し、ステージ選択を現実世界で行います! 入力方法はWiibordとmediapipe、全身を使って波に乗ります!

作った背景

波というテーマに着目してネットの海でサーフィンをしたいと考えたから。 本物のネットサーフィンを体験できます!

推し技術

LiveKitを使ったWebRTCリアルタイム対戦と、Wii Balance BoardのBluetooth HID通信をブラウザから直接制御し、体重移動でゲームを操作する仕組みを実装しました!

プロジェクト詳細

概要

このアプリはwifiを集めて取得したssidや電波強度の情報からサーフィン用のステージを作成して、そのステージでwiibordやMediaPipeを使ってサーフィンをします

押しポイント

wifiを収集するというアイデアからwifiを取得しやすいようにAndroidデバイスで周囲のssidと電波強度を取得してfirebaseに保存!

これによりゲームのステージ作成のためのwifiをスマホで集めて、ゲームはWEB上で遊ぶことが可能!

画面遷移図

各画面

認証画面

mobileのwifi取得ページimage ホーム image ゲーム画面

ゲームオーバー

wiiBord補正画面

マッチング用ルーム

対戦用ゲーム画面

ゲーム概要

MediaPipeで指定されたポーズを取ってScoreを稼ぎつつ、WiiBordでバランスを取って転覆しないように耐えながらハイスコアを目指そう!

技術スタック

フロントエンド

  • フレームワーク: React (Vite)
  • 3D描画: React Three Fiber / Three.js
  • 人物合成: MediaPipe(Pose + Segmentation)
  • WebRTC: livekit-client
  • 認証・DB: Firebase Authentication / Firestore

モバイル(React Native)

  • フレームワーク: React Native
  • WiFiスキャン: react-native-wifi-reborn
  • データ共有: Firebase Firestore(Webと同一プロジェクト)
  • 認証: Firebase Authentication

バックエンド

  • 実行環境: Firebase Cloud Functions v2
  • WebRTCインフラ: LiveKit Cloud

ブラウザAPI

  • Web HID API — Wii Balance Board 直接制御
  • getUserMedia API — MediaPipe用カメラ映像取得
  • Network Information API — リアルタイム回線速度取得

モバイルアプリ(React Native)

ゲームのステージはWiFiの通信速度で決まるため、良いWiFi(波・ステージ)を探して外に出るという体験を成立させるためにReact Nativeでモバイルアプリも開発しました。

Webブラウザの Network Information API では接続中のWiFiしか測定できませんが、React Nativeでは端末のAndroid WifiManager APIを叩くことで周辺に飛んでいる全WiFiのSSIDと電波強度(RSSI)を一覧取得できます。RSSIはdBmからMbpsに変換してゲームの波パラメータに反映します。

モバイルアプリでできること

  • 周辺WiFiをスキャンして一覧表示(難易度フィルター付き)
  • 気に入ったWiFiを選んで Firebase Firestoreに保存
  • 保存したWiFiはブラウザ版ゲームのステージ選択画面に反映される
  • ランキングの閲覧
  • Webとモバイルで同じFirebaseプロジェクト・同じFirestoreを共有しているため、スマホで外のWiFiを登録 → PCでゲームを起動という流れがシームレスに繋がります。

ステージ生成ロジック

接続しているWiFiの情報をもとに、波のパラメータをリアルタイムで生成します。

電波強度(RSSI)→ 波の間隔
電波が強いほど波と波の間隔が狭くなります。間隔が狭いほど次の波への対応が素早く求められます。

SSIDの文字画数 → 波の高さパターン
SSIDを1文字ずつ画数に変換し、その配列が波ごとの高さパターンになります。

漢字については日本語の漢字情報を提供する KanjiAlive API を利用して画数を取得しています。ゲーム開始前に非同期でAPIを叩いて結果をキャッシュしておくことで、ゲーム中は追加の通信なしに即時参照できるようにしています。

変換した画数の配列が波の高さパターンとして使われます。画数の大きい文字が多いSSIDほど高低差の激しい複雑な波形に、シンプルな英数字SSIDほど単調な波形になります。

SSID: "大波-5G" 大(3画) 波(8画) -(1画) 5(2画) G(2画) → [3, 8, 1, 2, 2] → 波1が低め・波2が高め・波3が低い... と繰り返す

難易度ラベル:

< 5 Mbps → 湖のように穏やか < 20 Mbps → 穏やか < 50 Mbps → 普通 < 80 Mbps → 荒れ それ以上 → 嵐

スコアへの反映:
ステージの難易度が高いほどスコア倍率が上がります。バランスを維持し続けるだけでも倍率に応じたスコアが加算され続けるため、良いWiFiを探して挑むほど高スコアが狙えます。

Wii Balance Board — WebHIDによるブラウザ直接制御

Wii Balance BoardをWebHIDで実装している記事が無かったので辛かったです

通信の流れ:

ブラウザ (WebHID API) ←→ Bluetooth HID ←→ Wii Balance Board └─ 4点荷重センサー(TopLeft / TopRight / BottomLeft / BottomRight)

接続・初期化シーケンス:

navigator.hid.requestDevice({ filters: [{ vendorId: 0x057e, productId: 0x0306 }] }) → device.open() → sendReport(0x11, [0x10]) // LED 1点灯(スリープ防止) → sendReport(0x12, [0x04, 0x32]) // レポートモード 0x32(拡張コントローラ)に設定 → 10秒ごとに keepalive でレポートモードを再送(Bluetooth切断・レート低下防止)

データ受信(レポートID 0x32):

payload byte 0-1 : ボタン状態など(スキップ) payload byte 2-3 : TopRight センサー値 — getUint16(0, false) ← 16bit unsigned big-endian payload byte 4-5 : BottomRight センサー値 — getUint16(2, false) payload byte 6-7 : TopLeft センサー値 — getUint16(4, false) payload byte 8-9 : BottomLeft センサー値 — getUint16(6, false)

3Dグラフィックス

React Three Fiberで3Dの海を描画します。 波の頂点計算はGLSLシェーダーでGPU側に処理を移すことで軽量化しています。 人物の3Dシーンへの合成:MediaPipeをGPUモードで毎フレーム実行し、カメラ映像から人体だけを切り抜いたオフスクリーン canvas を生成しまていす。その canvas を THREE.CanvasTexture としてポリゴンに貼り付け、背景透過のまま3Dシーンに合成。毎フレーム texture.needsUpdate = true を呼んでGPUへ転送することで、カメラ映像がリアルタイムでポリゴンに反映されます。 image (本来は全身を映すことでwiiboardの上に立っているように見える)

相手映像の3Dシーンへの合成

image (wi-fiマークは顔隠し用に後からスクショに追加)

相手の映像は、送信側で人物だけを切り抜いてグリーンバック化し、受信側でGPUシェーダーのクロマキーで緑を抜いて、3D空間の平面に合成しています。


対戦モード

LiveKit(WebRTC)でリアルタイム2人対戦ができます。

  • ルーム作成 / 参加: Firebase Cloud Functions + LiveKit Room APIでロビーを管理
  • ステージ選択: ホストが自分のWiFi環境を選んでステージを決定、ゲスト側に同期
  • スコア同期: LiveKitのデータチャンネルで1秒ごとにスコアをリアルタイム送信
  • 相手カメラ: WebRTCのビデオトラックで相手の表情を見ながら対戦
  • 再戦システム: 両者が合意した場合のみ再戦(どちらかが拒否するとロビーに戻る)

技術の無駄使い

Wii Balance Boardをブラウザから直接動かした点です ゲームコントローラーとしてほぼ誰も使わないWii Balance BoardをWebHID APIでブラウザから直接Bluetooth接続し、4点の荷重センサーから重心座標を計算してサーフィン操作に使っています。 WebHIDでwiibordを利用した記事が見つからなかったため、Wiimoteのリバースエンジニアリングドキュメントを参照し、受け渡しのデータの型を想定してデータの受け渡しをしました。

開発の進め方

Naitive初めて使いました チームメンバーのうちの一人は就活で事前開発無理でした miroというオンラインホワイトボードアプリを使用してアイデアから設計までしました。 技術選定は完成度を上げるために工数が少なそうなものを選択しました(コア機能以外)

miroの使用例

画面遷移 image

使用技術の選定 image

レイアウトの構想 image

開発メンバー

わしじゃよ:フロントエンド・バックエンド・モバイル(mobileでのwifi関係、wiibordのWebHID実装、WebRTCでカメラとScoreの双方向の通信確立、firebase関係、デプロイ) メギド:フロントエンド(three.js,wiibordの補正) 伊勢田:フロントエンド(mediapipe関連のゲームのメインギミック、ゲーム画面UI、スコア計算)


技術スタック

image image

わしじゃよ

@washijayo