xrift-world について
xrift-worldは、XRift向けWebXRワールドを作成・変更するための開発ガイドです。React Three Fiber開発者向けに、プラットフォーム固有の実装ルール、デバイス対応要件、設定、コード参照をまとめています。
主な機能
- 01XRift APIと型のリファレンス
- 02アセット読み込みと配置ルール
- 03PC・モバイル対応の指針
- 043D操作とUIのパターン
- 05物理・カメラ設定
活用シーン
- 01XRift WebXRワールドの作成
- 02モバイルやVR向け操作の調整
- 03ワールドの物理とカメラの設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ webxr-jp/ xrift-skills --skill xrift-worldインストール後、Claude Code で/xrift-worldを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-reference.mdcode-templates.mdtype-definitions.md詳細
XRift ワールド開発ガイド
XRift プラットフォーム向けの WebXR ワールドを作成・変更するためのガイドです。
参考資料
- API リファレンス -
@xrift/world-componentsのすべてのフック、コンポーネント、定数の完全な仕様 - コードテンプレート - GLB モデル、テクスチャ、Skybox、インタラクションなどの実装パターン
- 型定義 - User、PlayerMovement、VRTrackingData、TeleportDestination、WorldInfo、InstanceInfo、ConfirmOptions、Tag、VideoState、LogEntry、PhysicsConfig、CameraConfig、VoiceVolumeOverrideContextValue の型定義
重要なルール(必ず守ってください)
- アセットを読み込むときは、必ず
useXRift()のbaseUrlを使用してください - アセットファイルは
public/ディレクトリに配置してください baseUrlには末尾の/が含まれるため、${baseUrl}pathの形式で結合してください(${baseUrl}/pathは誤りです)
// Correct
const { baseUrl } = useXRift()
const model = useGLTF(`${baseUrl}robot.glb`)
// Wrong
const model = useGLTF('/robot.glb') // Absolute path - NG
const model = useGLTF(`${baseUrl}/robot.glb`) // Extra / - NG
デバイス対応(必ず守ってください)
訪問者は PC、モバイル(スマートフォンとタブレット)、VR ヘッドセットから同じインスタンスに参加します。**ワールドは、少なくとも PC とモバイルの両方で動作する必要があります。**可能な限り VR にも対応してください。
| デバイス | 対応 | 操作方法 |
|---|---|---|
| PC | 必須 | マウス+キーボード、中央の照準+クリック |
| モバイル | 必須 | タッチ、仮想ジョイスティック |
| VR | できる限り対応 | コントローラー、レイ+トリガー |
開発サーバー(npm run dev)では PC 操作しか試せないため、次のような問題を見落としがちです。
- **独自のキーボードショートカット(
window.addEventListener('keydown'))に機能を依存させないでください。**モバイルと VR にはキーボードがないため、それらの機能は使えなくなります。W/A/S/D、Space、Q/E、G、H、Esc などのキーもプラットフォームによって使用されています。- ボタンとスイッチ:
Interactableでラップします(PC ではクリック、モバイルではタッチ、VR ではコントローラーで操作) - 乗り物:
Seat/VehicleのonControlInputを使用します(キーボード、仮想ジョイスティック、サムスティックの入力はすべて同じ形式で届きます) - テキスト入力:
TextInput。確認:useConfirm - ショートカットは、
Interactableからも実行できるアクションについて、PC での利便性を高める目的でのみ使用できます
- ボタンとスイッチ:
- **DOM 要素で HUD や UI を作成しないでください。**DOM オーバーレイは、画面の小さいモバイル端末や画面の向きを変えたモバイル端末で表示が崩れ、プラットフォームの仮想ジョイスティックやボタンに重なります。VR では 3D シーンのみがレンダリングされるため、DOM UI は一切表示されません。
- UI は 3D 空間のオブジェクトとして配置します。
@react-three/dreiのText、パネルやメニューには@react-three/uikitを使用します - 日本語テキストには
useDefaultFontを読み込み、プレイヤーの方を向くようにBillboardYでラップし、クリック可能にするにはInteractableでラップします
- UI は 3D 空間のオブジェクトとして配置します。
- **進行を
Grabbableだけに依存させないでください。**現在、つかむ操作が機能するのは PC のみです(ポインターロック+照準)。
プロジェクトの概要
- 目的:XRift プラットフォーム向けの WebXR ワールド
- 技術スタック:React Three Fiber + Rapier 物理エンジン + Module Federation
- 動作の仕組み:CDN にアップロードされ、フロントエンドによって動的に読み込まれます
プロジェクト構成
xrift-world-template/
├── public/ # Asset files (place directly, no subdirectories needed)
│ ├── model.glb
│ ├── texture.jpg
│ └── skybox.jpg
├── src/
│ ├── components/ # 3D components
│ ├── World.tsx # Main world component
│ ├── dev.tsx # Development entry point
│ ├── index.tsx # Production export
│ └── constants.ts # Constants
├── .triplex/ # Triplex (3D editor) config
├── xrift.json # XRift CLI config
├── vite.config.ts # Build config (Module Federation)
└── package.json
xrift.json の設定
physics(物理設定)
| フィールド | 型 | デフォルト | 説明 |
|---|---|---|---|
gravity | number | 9.81 | 重力の強さ(正の値。地球=9.81、月=1.62、木星=24.79) |
allowInfiniteJump | boolean | true | 無限ジャンプを許可するかどうか |
{
"physics": {
"gravity": 9.81,
"allowInfiniteJump": true
}
}
例:
- 障害物コースのワールド:落下のリスクを加えるには
"allowInfiniteJump": falseを設定します - 低重力のワールド:浮遊感のある動きにするには
"gravity": 1.62(月の重力)を設定します - 高重力のワールド:重みのある動きにするには
"gravity": 24.79(木星の重力)を設定します
camera(カメラ設定)
| フィールド | 型 | 説明 |
|---|---|---|
near | number | ニアクリップ距離(この距離より近いオブジェクトを非表示にします) |
far | number | ファークリップ距離(この距離より遠いオブジェクトを非表示にします) |
{
"camera": {
"near": 0.1,
"far": 1000
}
}
例:
- 広大なワールド:遠くのオブジェクトをレンダリングするには
"far": 5000を設定します - 精密なワールド:近距離のレンダリング精度を高めるには
"near": 0.01を設定します
outputBufferType(出力バッファーの種類)
WebGLRenderer の出力バッファーの種類を指定します。ポストプロセス処理と HDR レンダリングの精度に影響します。
| 値 | 説明 |
|---|---|
UnsignedByteType | 8 ビット整数(デフォルト、標準的なレンダリング) |
HalfFloatType | 16 ビット浮動小数点数(HDR とポストプロセス処理) |
FloatType | 32 ビット浮動小数点数(最高精度、GPU 負荷は高め) |
{
"outputBufferType": "HalfFloatType"
}
items(最初から配置するアイテム)
<Item itemId> を使用してワールドに配置する、ユーザーが作成したアイテムの ID です。本番環境では、ここに記載された ID のアイテムのみが読み込まれます(入室時に事前読み込みされ、訪問者はこのリストからバンドルの URL を解決します)。自分で作成したアイテム、またはライブラリに追加したアイテム(マーケットプレイスで入手したもの)のみ宣言できます。最大 50 個です。
{
"items": [
"2a69ded4-d913-4359-8c1f-eac83a982b0c"
]
}
ローカル開発の場合は、@xrift/sdk/vite の xriftDev() を vite.config.ts に追加し、xrift login を実行します。これにより、DevEnvironment は開発サーバーのプロキシ(/__xrift)を介して、本番環境と同じバンドルを読み込みます。アイテムとワールドを同時に開発するには、<DevEnvironment items={{ [itemId]: Component }}> にローカルコンポーネントを渡します。
permissions(権限設定)
ワールドが必要とする権限を宣言します。宣言した権限は、インスタンスへの入室時に承認画面としてユーザーに表示されます。
| フィールド | 型 | 説明 |
|---|---|---|
allowedDomains | string[] | ワールドが通信する外部ドメイン |
allowedCodeRules | string[] | 緩和するコードセキュリティルール |
{
"permissions": {
"allowedDomains": ["api.example.com", "cdn.example.com"],
"allowedCodeRules": ["no-storage-access", "no-network-without-permission"]
}
}
allowedCodeRules
@xrift/code-security アナライザーで定義されるルールです。デフォルトでは安全でない操作はブロックされますが、必要に応じて制限を緩和できます。
| カテゴリ | ルール | 説明 |
|---|---|---|
| 動的コード | no-eval | eval() を許可します |
| 動的コード | no-new-function | Function コンストラクターを許可します |
| 動的コード | no-string-timeout | 文字列引数を指定した setTimeout/setInterval を許可します |
| 動的コード | no-javascript-blob | JavaScript Blob の作成を許可します |
| 難読化 | no-obfuscation | 難読化されたコードパターンを許可します |
| ネットワーク | no-network-without-permission | fetch、WebSocket などを許可します |
| ネットワーク | no-unauthorized-domain | allowedDomains に含まれないドメインへの接続を許可します |
| ネットワーク | no-rtc-connection | WebRTC ピア接続を許可します |
| ネットワーク | no-external-import | 外部 JavaScript モジュールのインポートを許可します |
| ストレージ | no-storage-access | localStorage/sessionStorage の使用を許可します |
| ストレージ | no-cookie-access | Cookie の読み書きを許可します |
| ストレージ | no-indexeddb-access | IndexedDB へのアクセスを許可します |
| ストレージ | no-storage-event | ストレージイベントのリスニングを許可します |
| DOM | no-dangerous-dom | innerHTML とスクリプトの挿入を許可します |
| ブラウザー API | no-navigator-access | 位置情報、カメラ、マイク、クリップボードの使用を許可します |
| グローバル | no-sensitive-api-override | fetch などの上書きを許可します |
| グローバル | no-global-override | window、document の上書きを許可します |
| グローバル | no-prototype-pollution | プロトタイプの変更を許可します |
コマンドリファレンス
# Development
npm run dev # Start dev server (http://localhost:5173)
npm run build # Production build
npm run typecheck # Type checking
# XRift CLI
xrift login # Authenticate
xrift create world # Create new world project
xrift upload # Upload (auto-detect from xrift.json)
xrift whoami # Check logged-in user
xrift logout # Log out
開発環境
npm run dev を実行して開発サーバーを起動します。一人称視点でワールド内を移動してテストできます。
| 操作 | キー |
|---|---|
| 周囲を見る | クリックしてマウスをロックし、マウスを動かします |
| 移動 | W / A / S / D |
| 上昇/下降 | E または Space / Q |
| インタラクト | 照準を合わせてクリックします |
| マウスロックを解除 | ESC |
Interactable コンポーネントのクリック動作も開発環境でテストできます(中央の Raycaster が LAYERS.INTERACTABLE レイヤーを検出します)。Grabbable も動作します。G でつかんで配置し、マウスホイールで距離を調整して、クリックで確定します。
Seat と Vehicle は開発環境で登録されるだけです。着席にはアバター、カメラ、物理演算が必要なため、ワールドを XRift 上で実行するまでは何も起こりません。
dev.tsx の構成
src/dev.tsx は開発専用のエントリーポイントです。本番ビルドには含まれません。
注:本番環境では XRiftProvider は必要ありません(フロントエンドが自動的にラップします)。
依存関係
Required (peerDependencies)
react/react-dom^19.0.0three^0.182.0@react-three/fiber^9.3.0@react-three/drei^10.7.3@react-three/rapier^2.1.0
XRift-specific
@xrift/world-components- XRift hooks and components
Module Federation Shared パッケージ
ホスト(xrift.net)と shared で共有されるパッケージ。ワールドの vite.config.ts で shared に宣言すれば、ワールドチャンクにバンドルされずホストから提供される。
| パッケージ | バージョン要件 |
|---|---|
react | ^19.0.0 |
react-dom | ^19.0.0 |
react-dom/client | - |
react/jsx-runtime | ^19.0.0 |
three | ^0.176.0 |
three/addons/loaders/GLTFLoader.js | - |
three/addons/loaders/DRACOLoader.js | - |
three/addons/loaders/KTX2Loader.js | - |
@react-three/fiber | ^9.0.0 |
@react-three/rapier | ^2.0.0 |
@react-three/drei | ^10.0.0 |
@react-three/uikit | ^1.0.0 |
@pmndrs/uikit | ^1.0.0 |
@xrift/world-components | ^0.1.0 |
three/addons の注意
three/addonsバレルを shared にすると Lottie 由来のevalがバンドルに含まれるため、サブパス単位で shared にしている- ワールド側でも
three/addons/loaders/DRACOLoader.jsのようにサブパスで import & shared 宣言する
// vite.config.ts — shared の設定例(xrift-world-template 準拠)
federation({
name: 'xrift_world_template',
filename: 'remoteEntry.js',
exposes: {
'./World': './src/index.tsx',
},
shared: {
react: { singleton: true, requiredVersion: '^19.0.0' },
'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
'react-dom/client': { singleton: true },
'react/jsx-runtime': { singleton: true },
three: { singleton: true, requiredVersion: '^0.176.0' },
// three/addons はバレルではなくサブパス単位で宣言する
'three/addons/loaders/DRACOLoader.js': { singleton: true },
'@react-three/fiber': { singleton: true, requiredVersion: '^9.3.0' },
'@react-three/rapier': { singleton: true, requiredVersion: '^2.1.0' },
'@react-three/drei': { singleton: true, requiredVersion: '^10.7.3' },
'@xrift/world-components': { singleton: true, requiredVersion: '^0.1.0' },
},
}),
Troubleshooting
"useXRift must be used within XRiftProvider"
Cause: Not wrapped with XRiftProvider
Solution:
- Check that
src/dev.tsxusesXRiftProvider - When using Triplex: check
.triplex/provider.tsx
Assets fail to load
Cause: Not using baseUrl, or incorrect path concatenation
Solution:
// Correct
const { baseUrl } = useXRift()
const model = useGLTF(`${baseUrl}robot.glb`)
// Wrong
const model = useGLTF('/robot.glb')
const model = useGLTF(`${baseUrl}/robot.glb`)
Physics not working
Cause: Not wrapped with Physics component, or missing RigidBody
Solution:
<Physics>
<RigidBody type="fixed"> {/* or "dynamic" */}
<mesh>...</mesh>
</RigidBody>
</Physics>
Links
Example Implementations
- GLB model:
src/components/Duck/index.tsx - Skybox:
src/components/Skybox/index.tsx - Animation:
src/components/RotatingObject/index.tsx - Interaction:
src/components/InteractableButton/index.tsx - User tracking:
src/components/RemoteUserHUDs/index.tsx - Teleport:
src/components/TeleportPortal/index.tsx - Main world:
src/World.tsx