[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」で開く。localStorage の friend_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 に保存されていた。修正後にポータルを消して再配置することで解決。
menu.js — ZIP export に Works Portal 追加
ZIP export 時に works_portal_list も portal_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.js | Works Portal メニュー追加 + portalType 判定修正 |
js/corridor.js | 広場のポータルタイプ別分岐 + 色マッピング |
js/menu.js | ZIP export に portal_list_works.json 追加 |
js/controls.js | enterCorridor に portalType 受け渡し |
config.js | PORTAL_COLORS に works (#ffaa00) 追加 |
index.html | ポータルエディタ用 HTML 追加 |
style.css | ポータルエディタ用スタイル追加 |
README.md | Portal Types / メニュー / ディレクトリ / 登録案内 更新 |
次回タスク
掲示板機能の実装
- portal 用の各 JSON に掲示板告知用セクションを追加し、情報があれば広場の掲示板に表示
- 情報が多い場合はスクロール対応
- 各広場(Global/Friend/Works)に応じて掲示板内容を表示分け
現在の状態
3種のポータルが完成し、WIRED ROOM のコア機能が全て揃った。部屋作成→カスタマイズ→エクスポート→公開→ポータル接続→VR対応。静的ファイルだけで完結する個人3D空間プラットフォーム。