[JavaScript] 3D ROOM制作 5日目 VRヒント・バナー画像・Import/Export
本日の作業
WIRED ROOM 制作5日目です。今日は VR の UI改善、広場のビジュアル強化、そしてデータ管理機能を一気に実装しました。
前回の記事:[3D ROOM制作 4日目 プログレスバー・ポータル改修・広場実装]
[JavaScript] 3D ROOM制作 4日目 プログレスバー・ポータル改修・広場実装 // PROTOCOL.LAIN
WIRED ROOM開発4日目。プログレスバー実装、config.js/portal-effect.jsへの共通化、app.jsのinit分解(loadRoom/disposeRoom/loadExternalRoom)、プロシージャル十字路を廃止してKenney素材の広場に置き換え、VRポータル操作、Quest 3問題の調査記録
lain-lab.comリポジトリ:[github.com/fixtan/WIRED_ROOM]
GitHub - fixtan/WIRED_ROOM: A 3D walkable room you can deploy on any web server. No build tools. Vanilla JS + Three.js + WebXR.
A 3D walkable room you can deploy on any web server. No build tools. Vanilla JS + Three.js + WebXR. - fixtan/WIRED_ROOM
github.com1. クレジットボード修正
エディタの表記を調整しました。
- ラベルを「Credit List」→「Message」に変更
- プレースホルダーを「お知らせ、備考、クレジットなど自由入力」に
- テキストエリアの行数を 4 → 6 に拡大
- ボード上の「── Credits ──」ヘッダーを削除
クレジット専用ではなく、自由にメッセージを書ける汎用ボードとして再定義しました。Canvas の高さは行数に応じて動的に伸びるので、長文でも問題ありません。
2. VR ポータル近接ヒント(vr-ui.js)
PC では、ポータルに近づくと [E] global のようなテキストが画面に表示されます。しかし VR ではこの HTML オーバーレイはヘッドセットの中から見えません。
そこで、VR 専用の 3D ヒントスプライトを実装しました。
新規ファイル: js/vr-ui.js
汎用的な VR ヒント表示モジュールです。Canvas テクスチャで [A] ENTER のようなテキストを描画し、Sprite として 3D 空間上に配置します。
// API
initVRHint(scene) // シーンに初期化
showVRHint(pos, text, offset?) // 表示(テキスト変更時のみテクスチャ再生成)
hideVRHint() // 非表示
disposeVRHint() // 破棄
controls.js の checkPortalProximity 内で isVRActive() を判定し、VR 時は HTML ではなく 3D スプライトを表示するようにしました。
ポイントは、ポータル専用ではなく汎用モジュールとして設計したことです。今後、他のオブジェクトとのインタラクション表示にもそのまま使えます。
3. 広場バナー画像
広場のポータル上に表示されるラベルが、テキストだけでは味気なかったので、バナー画像の表示機能を追加しました。
仕組みs
portal_list.json に image フィールドを追加。画像 URL が指定されていれば TextureLoader で非同期に読み込み、テキストラベルをバナースプライトに差し替えます。読み込み失敗時はテキストラベルのままフォールバックします。
{
"name": "lain's room",
"url": "https://lain-lab.com/room/",
"image": "https://lain-lab.com/room/banner.webp",
"description": "メインルーム"
}
バナー画像の仕様
- 推奨サイズ: 480 × 120 px(4:1)
- 形式: WebP または PNG、30KB以下推奨
- CORS が通らないサーバーの画像は自動的にテキストにフォールバック
CORS 対応
クロスオリジンでバナーを読み込むため、Netlify の場合は public/_headers に以下を追加します。
/room/*
Access-Control-Allow-Origin: *
Netlify、GitHub Pages、Vercel、Cloudflare Pages はデフォルトで CORS ヘッダーが付くので、多くの場合は追加設定不要です。
Base64 埋め込みの検討と却下
CORS 問題を完全に回避するため、バナーを Base64 で portal_list.json に直接埋め込む案も試しました。480×120 WebP で約 15KB。1 サイトなら問題ありませんが、20 サイト登録されると 300KB になり、portal_list.json が肥大化して現実的ではありません。
結論として、外部 URL + フォールバックが最もバランスの良い方式です。
結果
全ルームにバナーを作成して並べたところ、広場の印象が一変しました。テキストラベルだけの時とは別物です。
4. Import ZIP / Clear All Data
WIRED ROOM のデータはブラウザの localStorage と IndexedDB に保存されています。これまで Export ZIP でデータを書き出すことはできましたが、復元する手段がありませんでした。
Import ZIP
エクスポートした ZIP ファイルを読み込んで、ルームを完全に復元します。
処理の流れ:
- ZIP を読み込み、
public/manifest.jsonをパース - 警告ダイアログ(既存データの上書き確認)
- 既存データを全クリア(IndexedDB + localStorage)
- room.glb → IndexedDB に保存
- 画像 → Base64 data URL に変換して IndexedDB に保存
- 動画 → ArrayBuffer のまま IndexedDB に保存
- VRM フィギュア + ポーズ → IndexedDB に保存
- クレジットデータ → roomMeta に復元
- room_config + room_media → localStorage に書き込み
- リロード → 完全復元
Clear All Data
localStorage と IndexedDB の全データを削除します。2 段階の確認ダイアログで誤操作を防止。削除後はセットアップウィザードが再表示されます。
ルーム切り替えワークフロー
この 2 つの機能を組み合わせることで、複数のルームを自由に切り替えられるようになりました。
- 現在のルームを Export ZIP でバックアップ
- Clear All Data で初期化
- 別の ZIP を Import ZIP で復元
ビルドツールもサーバーも不要で、ZIP ファイルだけでルームがポータブルになります。
今日の成果まとめ
| 項目 | 内容 |
|---|---|
| クレジットボード | ラベル・ヘッダー修正、汎用メッセージボード化 |
| vr-ui.js | VR 近接ヒント表示(汎用 3D スプライト) |
| バナー画像 | corridor.js 拡張、CORS 対応、全 5 ルーム分作成 |
| Import ZIP | エクスポート ZIP からの完全復元 |
| Clear All Data | IndexedDB + localStorage 全消去 |
| README.md | VR 操作、バナー仕様、Backup/Restore セクション追加 |
所感
今日の作業で WIRED ROOM が「ポータブルな 3D ポートフォリオ空間」として形になってきた実感があります。
Export → Clear → Import のラウンドトリップが一発で通ったのは、エクスポートの ZIP 構造がきちんと設計されていたおかげです。インポートは逆変換するだけで済みました。
バナー画像を全ルームに並べた時の見た目のインパクトも大きかったです。テキストラベルだけの広場とは別物でした。
過去に作ったゲームや作品の紹介ルームを個別に作って、ポータルで繋げれば、個人の作品群が一つの世界になります。そういう使い方ができるプラットフォームは今のところ他にないと思います。
リポジトリ: github.com/fixtan/WIRED_ROOM
GitHub - fixtan/WIRED_ROOM: A 3D walkable room you can deploy on any web server. No build tools. Vanilla JS + Three.js + WebXR.
A 3D walkable room you can deploy on any web server. No build tools. Vanilla JS + Three.js + WebXR. - fixtan/WIRED_ROOM
github.com