[JavaScript] 3D ROOM制作 4日目 プログレスバー・ポータル改修・広場実装

[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.jsavatar.jsloadGLB/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.jstryPortalInteractcheckPortalProximityを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/loadExternalRoom
  • js/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 — プログレスバーHTML
  • style.css — プログレスバーCSS

おわりに

朝は休んでいましたが、午後からの集中で大量の実装が進みました。暗い通路が明るい広場に変わり、VRでのポータル操作も動くようになり、app.jsの設計も再利用可能な形に整理されました。Quest 3の問題は未解決ですが、PC・Quest 2では全機能が動作しています。