[JavaScript] 3D ROOM制作 7日目 Friend/Works Portal・ポータルエディタ実装

[JavaScript] 3D ROOM制作 7日目 Friend/Works Portal・ポータルエディタ実装

今日やったこと

Friend Portal と Works Portal の実装。これで WIRED ROOM のポータルシステムが3種類に分かれた。

3種類のポータル

タイプデータソース用途
Global緑 (#00ff88)portal_list.json(GitHub)公開ディレクトリ — 全ROOM共通
Friend青 (#4488ff)portal_list_private.json / localStorageルーム間のプライベートリンク
Works黄 (#ffaa00)portal_list_works.json / localStorage自分の作品群をポートフォリオとして接続

ルーム内にポータルを置くと、踏んだときにそのタイプ専用の広場が生成される。Global なら GitHub 上の公開リストからランダムに、Friend なら portal_list_private.json から、Works なら portal_list_works.json から読み込む。

portal-editor.js — ポータルリスト編集UI

新規ファイル portal-editor.js を作成。Friend と Works のポータルリストを編集する共通UIコンポーネント。

各エントリに以下の4フィールド:

  • Name(ルーム名)
  • URL(リンク先)
  • Banner Image(バナー画像URL)
  • Description(説明文)

右クリックメニューから「Edit Friend List」「Edit Works List」で開く。localStoragefriend_portal_list / works_portal_list に保存。

openPortalEditor(type) で friend/works を切り替えるだけの設計にしたので、コードの重複はゼロ。

corridor.js — 広場のポータルタイプ別表示

placePortals() を改修。portalType 引数で分岐:

if (portalType === 'friend' || portalType === 'works') {
  const jsonFile = portalType === 'friend'
    ? './public/portal_list_private.json'
    : './public/portal_list_works.json';
  const storageKey = portalType === 'friend'
    ? 'friend_portal_list'
    : 'works_portal_list';
  // ...
}

色もタイプ別にマッピング:

const colorMap = { global: '#00ff88', friend: '#4488ff', works: '#ffaa00' };
const color = colorMap[portalType] || '#00ff88';

公開モードでは ./public/portal_list_*.json を先に読み、なければ localStorage にフォールバック。

controls.js — portalType の受け渡しバグ修正

tryPortalInteract()enterCorridor(stateRef) を呼んでいたが、portalType を渡していなかった。これだと Friend/Works ポータルを踏んでも常に Global 広場が開く。

// Before
enterCorridor(stateRef);

// After
const portalType = promptEl.dataset.portalType || 'global';
enterCorridor(stateRef, portalType);

media.js — portalType 判定バグ修正

placePortal() 内の portalType 判定が friend しかチェックしていなかった:

// Before — works が global に落ちる
const portalType = savedData?.portalType || (label === 'friend' ? 'friend' : 'global');

// After — 全タイプ対応
const VALID_TYPES = ['global', 'friend', 'works'];
const portalType = savedData?.portalType || (VALID_TYPES.includes(label) ? label : 'global');

このバグのせいで、修正前に配置した Works ポータルは portalType: 'global' で localStorage に保存されていた。修正後にポータルを消して再配置することで解決。

ZIP export 時に works_portal_listportal_list_works.json として書き出すように追加:

let worksPortals = [];
try {
  worksPortals = JSON.parse(localStorage.getItem('works_portal_list') || '[]');
} catch (e) { /* ignore */ }
if (worksPortals.length > 0) {
  zip.file('public/portal_list_works.json', JSON.stringify(worksPortals, null, 2));
}

README 更新

  • Portal Types セクションを「(planned)」から実装済みテーブルに更新
  • コンテキストメニュー表に Friend/Works Portal 関連5項目追加
  • ディレクトリ構成に portal-editor.js 追加
  • localStorage テーブルに friend_portal_list / works_portal_list 追加
  • Registering Your Room セクション追加(contact フォームからのポータル登録案内)

変更ファイル

ファイル変更内容
js/portal-editor.js新規。Friend/Works ポータルリスト編集UI
js/media.jsWorks Portal メニュー追加 + portalType 判定修正
js/corridor.js広場のポータルタイプ別分岐 + 色マッピング
js/menu.jsZIP export に portal_list_works.json 追加
js/controls.jsenterCorridor に portalType 受け渡し
config.jsPORTAL_COLORS に works (#ffaa00) 追加
index.htmlポータルエディタ用 HTML 追加
style.cssポータルエディタ用スタイル追加
README.mdPortal Types / メニュー / ディレクトリ / 登録案内 更新

次回タスク

掲示板機能の実装

  • portal 用の各 JSON に掲示板告知用セクションを追加し、情報があれば広場の掲示板に表示
  • 情報が多い場合はスクロール対応
  • 各広場(Global/Friend/Works)に応じて掲示板内容を表示分け

現在の状態

3種のポータルが完成し、WIRED ROOM のコア機能が全て揃った。部屋作成→カスタマイズ→エクスポート→公開→ポータル接続→VR対応。静的ファイルだけで完結する個人3D空間プラットフォーム。