[JavaScript] 3D ROOM制作 4日目 プログレスバー・ポータル改修・広場実装
はじめに
WIRED ROOM の開発4日目です。今日は機能面の強化とリファクタリングを中心に進めました。
README.md の作成と公開
GitHubリポジトリにREADME.mdを作成しました。プロジェクト概要、セットアップ手順、操作方法、ディレクトリ構成、データストレージの仕様、ポータルシステムの設計など、WIRED ROOMを使うために必要な情報を一通りまとめています。
スクリーンショットはCDN(assets.lain-lab.com)から読み込む形にしました。リポジトリにバイナリを含めたくなかったのと、画像を差し替える際にURLを変えずに済むためです。
https://github.com/fixtan/WIRED_ROOM
ロードプログレスバーの実装
ROOM起動時の待ち時間が長い問題に対処しました。VRMファイルだけで10MB、GLBが数MB、その他アセットを合わせると30秒近く待たされることがあります。
ステージベースとバイトベースのハイブリッド方式を採用しました。各工程(Skybox → Room GLB → Objects → Avatar → Media → VR)にパーセンテージの区間を割り当て、GLBやVRMの読み込み中はローダーのonProgressコールバックから実際のダウンロード進捗を取得してバー内を滑らかに動かします。
room-loader.jsとavatar.jsのloadGLB/loadVRM関数にonProgress引数を追加するだけで済みました。変更は最小限です。
ポータルシステムのリファクタリング
URL入力の廃止とポータルタイプの導入
ポータル作成時のURL手入力を廃止しました。ポータルのURLはportal_list.jsonから自動取得する設計に変更したためです。
代わりにGlobal(公開)とFriend(非公開)の2種類のポータルタイプを導入しました。Globalは緑(#00ff88)、Friendはオレンジ(#ff8800)で色分けされます。Friend Portalの実装はまだですが、データ構造にはportalTypeフィールドを含めてあるので、後から追加するだけで動きます。
config.js — 定数の集約
散在していたハードコード値をconfig.jsに集約しました。ポータルの色、リングの数、パーティクル数、プログレスバーの配分、メディア調整パネルの移動ピッチなど、チューニングが必要な値が一箇所にまとまっています。
portal-effect.js — エフェクトの共通化
編集モード(media.js)と公開モード(app.js)で別々に書かれていたポータルエフェクト(リング+パーティクル)のコードをportal-effect.jsに統合しました。createPortalEffect(portalType)を呼ぶだけでエフェクト付きのGroupが返ってきます。
VRポータル操作の実装
Meta Quest のVRモードでポータル操作ができるようになりました。
controls.jsのtryPortalInteractとcheckPortalProximityをexportし、vr.jsからimportして、Aボタンでポータルインタラクト、毎フレームの近接判定を行います。ボタンのデバウンス処理(wasAPressed/wasYPressed)をモジュールスコープに置くことで、押しっぱなし連打を防いでいます。
app.js の設計見直し — init/loadRoom/disposeRoom
VRでのルーム間SPA遷移を実現するため、init()を3つの関数に分解しました。
- init() — Scene, Camera, Renderer, Controls, Menu, VRなどの一回限りのセットアップ
- loadRoom(S, roomData, showProgress) — Skybox, Lights, GLB, Avatar, Mediaの読み込み。初回起動時と外部ルーム遷移時の両方で使用
- disposeRoom(S) — 現在のルームのオブジェクトを全破棄(Geometry, Material, Textureのdispose)
_roomObjects配列でシーンに追加されたルーム関連オブジェクトを追跡し、disposeRoomでまとめて破棄します。Camera, CameraGroup, AvatarGroupは追跡から除外されるため消えません。
loadExternalRoom(S, url)は外部のmanifest.jsonをfetchし、CORSチェック → dispose → loadRoomの一連の流れを実行します。CORSチェックが失敗した場合は従来のページ遷移(window.location.href)にフォールバックします。
コリドーのアバター表示
コリドー(現在は広場)に入った際にアバターが消える問題を修正しました。原因はhideRoomがシーンの全オブジェクトをvisible=falseにしていたことで、S.avatarGroupをスキップする条件を追加して解決しました。
暗い通路から明るい広場へ
プロシージャル十字路を廃止し、Kenney.nlのCC0素材(block-grass-overhang-low-long.glb)を使った明るい広場に置き換えました。
- 地面:GLBをスケールアップして平らな芝生
- スカイボックス:青空(#55aaee)
- ポータル:正面に横一列(最大6個)、背後にRETURN
- 各ポータルにドアモデル(door-rotate.glb)を配置
- 右手に大型掲示板(WIRED ROOM NETWORK)
- 花と樽で装飾
- BVH衝突判定で地面の外に出られない
portal_list.jsonからランダムに最大6件を抽出して並べます。自分自身のURLは除外されます。
テクスチャの白飛び修正
renderer.toneMapping = ACESFilmicToneMappingが画像テクスチャにも適用されて白飛びしていた問題を、mat.toneMapped = falseで修正しました。
Quest 3 ブラウザの問題
Quest 3のブラウザでは、XRセッション中の外部fetch(GitHub raw URLなど)が制限されることが判明しました。Quest 2やPCVRでは問題ありません。prefetchやローカルフォールバックを試みましたが完全な解決には至らず、要調査事項としてroadmap.mdに記録しています。
今日の変更ファイル
README.md— 新規作成config.js— 新規作成(定数集約)js/portal-effect.js— 新規作成(ポータルエフェクト共通化)app.js— init分解、プログレスバー、loadRoom/disposeRoom/loadExternalRoomjs/avatar.js— onProgress対応、disposeAvatar追加js/room-loader.js— onProgress対応js/corridor.js— 広場に全面書き換えjs/controls.js— tryPortalInteract/checkPortalProximity export、座標デバッグキーjs/vr.js— VRポータル操作、デバウンス修正js/media.js— ポータルURL削除、portalType追加、toneMapped修正index.html— プログレスバーHTMLstyle.css— プログレスバーCSS
おわりに
朝は休んでいましたが、午後からの集中で大量の実装が進みました。暗い通路が明るい広場に変わり、VRでのポータル操作も動くようになり、app.jsの設計も再利用可能な形に整理されました。Quest 3の問題は未解決ですが、PC・Quest 2では全機能が動作しています。