メインコンテンツまでスキップ

API リファレンス

xrift-world-components で提供されるコンポーネント、フック、定数の一覧です。

コンポーネント

Interactable

クリック/インタラクト可能なオブジェクトを作成します。

import { Interactable } from '@xrift/world-components';

<Interactable id="my-button" onInteract={() => console.log('clicked!')}>
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="hotpink" />
</mesh>
</Interactable>

Props

PropTypeDefaultDescription
idstring-一意の識別子(必須)
onInteract() => void-インタラクト時のコールバック
childrenReactNode-インタラクト対象のオブジェクト

Mirror

リアルタイム反射面を作成します。

import { Mirror } from '@xrift/world-components';

<Mirror position={[0, 1, -5]} />

VideoScreen

同期された動画再生を行うスクリーンを作成します。

import { VideoScreen } from '@xrift/world-components';

<VideoScreen src="/videos/intro.mp4" position={[0, 2, -3]} />

VideoPlayer

VideoScreen をベースにしたUIコントロール付きのビデオプレイヤーです。再生/停止ボタン、プログレスバー、音量バーなどVR対応のコントロールUIを備えています。

import { VideoPlayer } from '@xrift/world-components';

<VideoPlayer
id="my-video"
url="https://example.com/video.mp4"
position={[0, 2, -5]}
width={4}
/>

Props

PropTypeDefaultDescription
idstring-スクリーンの一意なID(必須)
position[number, number, number][0, 2, -5]スクリーンの位置
rotation[number, number, number][0, 0, 0]スクリーンの回転
widthnumber4スクリーンの幅(高さは16:9で自動計算)
urlstring-動画のURL
playingbooleantrue初期再生状態
volumenumber1初期音量(0〜1)
sync'global' | 'local''global'同期モード

機能

  • URL入力ボタン: 🔗 アイコンをクリックするとURL入力オーバーレイが表示され、動画ソースを動的に切り替え可能
  • 再生/停止ボタン: ▶/|| アイコンで再生状態を切り替え
  • プログレスバー: 20セグメントに分割された進捗バー。クリックで動画の最初に戻る
  • 音量バー: 0-100%を10刻みで調整。🔈/🔇アイコンでミュート状態を表示
  • VR対応: Interactable を使用したVRコントローラー操作に対応
同期モード

sync プロパティで同期モードを選択できます:

  • 'global': 全ユーザー間で再生状態を同期(デフォルト)
  • 'local': 各ユーザーが独立して再生を制御

LiveVideoPlayer

HLS/DASH などのライブストリーミング再生に対応したビデオプレイヤーです。VideoPlayer と同様のUIコントロールを備えつつ、ライブ配信向けに最適化されています。

import { LiveVideoPlayer } from '@xrift/world-components';

<LiveVideoPlayer
id="my-live"
url="https://example.com/live/stream.m3u8"
position={[0, 2, -5]}
width={4}
/>

Props

PropTypeDefaultDescription
idstring-スクリーンの一意なID(必須)
position[number, number, number][0, 2, -5]スクリーンの位置
rotation[number, number, number][0, 0, 0]スクリーンの回転
widthnumber4スクリーンの幅(高さは16:9で自動計算)
urlstring-ストリームのURL(HLS/DASH対応)
playingbooleantrue初期再生状態
volumenumber1初期音量(0〜1)

機能

  • URL入力ボタン: 🔗 アイコンをクリックするとURL入力オーバーレイが表示され、ストリームソースを動的に切り替え可能
  • 再生/停止ボタン: ▶/|| アイコンで再生状態を切り替え
  • 音量バー: 0-100%を10刻みで調整。🔈/🔇アイコンでミュート状態を表示
  • VR対応: Interactable を使用したVRコントローラー操作に対応
VideoPlayer との違い

LiveVideoPlayer はライブストリーミング向けに設計されているため、プログレスバー(シーク機能)がありません。録画済み動画の再生には VideoPlayer を使用してください。


ScreenShareDisplay

画面共有の映像を3D空間内にスクリーンとして表示します。ScreenShareContext から映像と状態を取得します。

import { ScreenShareDisplay } from '@xrift/world-components';

<ScreenShareDisplay id="screen-1" position={[0, 2, -5]} />

Props

PropTypeDefaultDescription
idstring-スクリーンの一意なID(必須)
position[number, number, number][0, 0, 0]スクリーンの位置
rotation[number, number, number][0, 0, 0]スクリーンの回転
widthnumber4スクリーンの幅(高さは16:9で自動計算)
アスペクト比の維持

映像のアスペクト比は自動的に維持されます。16:9以外の映像でも黒帯が入り正しく表示されます。

制限事項

共有できる画面はワールドにつき1つまでです。ScreenShareDisplay を複数配置することは可能ですが、すべて同じ画面が表示されます。


SpawnPoint

ワールド内でプレイヤーが出現する地点を指定します。

import { SpawnPoint } from '@xrift/world-components';

<SpawnPoint />
<SpawnPoint position={[0, 0, 5]} yaw={180} />

Props

PropTypeDefaultDescription
position[number, number, number][0, 0, 0]スポーン位置
yawnumber0スポーン時の向き(度数法 0-360)
開発時ヘルパー

開発環境では、半透明の円柱(下から上にかけて透明度が増すグラデーション)と矢印でスポーン位置と方向を視覚化します。本番ビルドではヘルパーは表示されません。

SpawnPoint ヘルパー

複数のSpawnPoint

複数の SpawnPoint を配置した場合、最後に設定されたものが有効になります。


TextInput

3D空間内でテキスト入力を可能にするコンポーネントです。children方式で外観を自由にカスタマイズできます。

import { TextInput } from '@xrift/world-components';

<TextInput
id="my-input"
value={inputValue}
onSubmit={handleSubmit}
placeholder="テキストを入力..."
>
<mesh>
<boxGeometry args={[1, 0.5, 0.1]} />
<meshStandardMaterial color="#333" />
</mesh>
</TextInput>

Props

PropTypeDefaultDescription
idstring-入力フィールドの一意なID(必須)
childrenReactNode-3Dオブジェクト(外観)(必須)
placeholderstring-プレースホルダーテキスト
maxLengthnumber-最大文字数
valuestring-現在の値
onSubmit(value: string) => void-入力完了時のコールバック
interactionTextstring'クリックして入力'インタラクション時に表示するテキスト
disabledbooleanfalse入力を無効にするか

仕組み

TextInput コンポーネントは以下のアーキテクチャで動作します:

  1. TextInput: children として渡された3Dオブジェクトをクリック可能な入力フィールドとして表示
  2. オーバーレイ入力: クリック時に2Dのテキスト入力UIがオーバーレイとして表示され、実際の入力を受け付けます
  3. XRiftContext連携: world-componentsはXRiftContext経由でオーバーレイ表示をリクエストします
外観のカスタマイズ

children に任意の3Dオブジェクトを渡すことで、入力フィールドの外観を自由にカスタマイズできます。ボタン風のデザインや、ワールドの世界観に合わせた見た目を実現できます。

関連するContext/Hook

プラットフォーム側では以下のAPIを使用してTextInputの動作を実装しています:

  • TextInputContext
  • useTextInputContext
  • TextInputContextValue
  • TextInputRequest

TagBoard

ユーザーが選択したタグをローカル/グローバルに扱い、ボードUI(TagSelector)と各ユーザー頭上へのタグ表示(TagDisplay)を提供するコンポーネントです。

import { TagBoard } from '@xrift/world-components';

<TagBoard
instanceStateKey="main-tag-board"
position={[0, 1.5, -3]}
/>

Props

PropTypeDefaultDescription
tagsTag[]デフォルトタグ一覧表示・選択対象のタグ
columnsnumber3表示列数
titlestring"タグ選択"タイトル文言
instanceStateKeystring-インスタンス状態のキー(必須、複数ボード設置時の識別用)
position[number, number, number][0, 0, 0]ボードの位置
rotation[number, number, number][0, 0, 0]ボードの回転
scalenumber1全体スケール

Tag 型定義

interface Tag {
id: string; // タグの一意識別子
label: string; // 表示ラベル
color: string; // 色(HEX形式)
}

デフォルトタグ一覧

tags プロパティを省略した場合、以下のタグが使用されます:

[
{ color: "#2ECC71", id: "want-talk", label: "話したい" },
{ color: "#3498DB", id: "want-listen", label: "聞きたい" },
{ color: "#95A5A6", id: "silent", label: "無言" },
{ color: "#1ABC9C", id: "developer", label: "開発者" },
{ color: "#2980B9", id: "student", label: "学生" },
{ color: "#F1C40F", id: "beginner", label: "初心者" },
{ color: "#9B59B6", id: "dont-know", label: "なんもわからん" },
{ color: "#8BC34A", id: "working", label: "作業中" },
{ color: "#BF7B41", id: "away", label: "離席中" },
{ color: "#FF9800", id: "cat", label: "ねこ" },
]

使用例

カスタムタグを使用
import { TagBoard, type Tag } from '@xrift/world-components';

const customTags: Tag[] = [
{ id: "frontend", label: "フロントエンド", color: "#61DAFB" },
{ id: "backend", label: "バックエンド", color: "#68A063" },
{ id: "design", label: "デザイン", color: "#FF6B6B" },
{ id: "pm", label: "PM", color: "#9B59B6" },
];

export const MyWorld = () => {
return (
<TagBoard
tags={customTags}
columns={2}
title="あなたの役割は?"
instanceStateKey="role-tag-board"
position={[0, 1.5, -3]}
rotation={[0, 0, 0]}
scale={1.2}
/>
);
};
複数のTagBoardを設置する場合

instanceStateKey は同一ワールド内で一意である必要があります。複数の TagBoard を設置する場合は、それぞれ異なる instanceStateKey を指定してください。

依存関係
  • UsersContext が必要です(ユーザー情報の取得に使用)
  • 内部で useInstanceState フックを使用しています(タグ選択状態の同期)

DevEnvironment

ローカル開発用の環境を提供するコンポーネントです。ワールドテンプレートの dev.tsx で使用します。

使用場所について

このコンポーネントはワールド開発プロジェクトで npm run dev を実行した際のローカル確認用です。World.tsx などの実際のワールドコンテンツ内では使用しないでください。

import { DevEnvironment, XRiftProvider } from '@xrift/world-components'
import { World } from './World'
import xriftConfig from '../xrift.json'

createRoot(rootElement).render(
<StrictMode>
<XRiftProvider baseUrl="/">
<DevEnvironment
physicsConfig={xriftConfig.world?.physics}
camera={{ near: xriftConfig.world?.camera?.near, far: xriftConfig.world?.camera?.far }}
>
<World />
</DevEnvironment>
</XRiftProvider>
</StrictMode>
)

Props

PropTypeDefaultDescription
childrenReactNode-ワールドコンテンツ(必須)
camera{ position?: [x, y, z]; fov?: number; near?: number; far?: number }{ fov: 50, near: 0.01, far: 1000 }カメラ設定
moveSpeednumber5.0移動速度
shadowsbooleantrueシャドウの有効/無効
spawnPosition[x, y, z][0.11, 1.6, 7.59]スポーン位置
respawnThresholdnumber-10リスポーンのY座標閾値
physicsConfigPhysicsConfig-物理設定

CameraConfig

camera prop で設定可能なクリッピング距離です。xrift.jsonworld.camera 設定と対応しています。

PropTypeDefaultDescription
nearnumber0.01nearクリッピング距離
farnumber1000farクリッピング距離

PhysicsConfig

PropTypeDefaultDescription
gravitynumber9.81重力加速度
allowInfiniteJumpbooleantrue無限ジャンプの許可

提供する機能

  • ファーストパーソンプレイヤー: 物理ベースのWASD移動・ジャンプ・リスポーン
  • 視点操作: PointerLockControls による視点操作
  • インタラクション: INTERACTABLE レイヤーへのレイキャスト + クリックインタラクション
  • クロスヘアUI: 画面中央のクロスヘア(ヒット時ハイライト)
  • 案内UI: ポインターロック状態の案内UI
  • 操作説明UI: 操作方法を表示するUI

操作方法

操作説明
クリックポインターロック開始 / インタラクト
WASD / 矢印キー移動
Space / Eジャンプ
ESCポインターロック解除
前提条件

@react-three/rapier^2.0.0)のインストールが必要です(optional peerDependency)。


Portal

他のインスタンスへの移動ポータルを表示するコンポーネントです。渦巻きシェーダーエフェクト、移動先のサムネイル・ワールド名・インスタンス名・人数、パーティクル、グロー、クリック可能な台座で構成されます。

instanceId を指定すると、対象インスタンスの情報を自動取得して表示します。台座をクリックすると確認モーダル(useConfirm)を経て対象インスタンスへ遷移します。

import { Portal } from '@xrift/world-components'

function MyWorld() {
return (
<Portal
instanceId="ceffb128-23c7-4120-b4e6-19bf6c604c47"
position={[5, 0, 0]}
rotation={[0, Math.PI / 2, 0]}
/>
)
}

Props

PropTypeDefaultDescription
instanceIdstring-移動先のインスタンスID(必須)
position[number, number, number][0, 0, 0]ポータルの座標
rotation[number, number, number][0, 0, 0]ポータルの回転
インスタンスIDの確認方法

インスタンスIDはインスタンスページのURLに含まれる UUID です。例えば https://app.xrift.net/instance/ceffb128-23c7-4120-b4e6-19bf6c604c47 の場合、ceffb128-23c7-4120-b4e6-19bf6c604c47 がインスタンスIDです。

内部で使用するフック

Portal は内部で useInstance フックを使用してインスタンス情報の取得と遷移を行っています。


BillboardY

子要素をカメラに対してY軸回転のみで追従させるコンポーネントです。drei の <Billboard> は全軸で回転しますが、BillboardY はY軸のみ回転するため「上方向」が維持されます。炎、パーティクル、ネームプレート、看板などに最適です。

Mirror(Reflector)と併用しても、鏡の中で正しい向きで表示されます。

import { BillboardY } from '@xrift/world-components'

function NamePlate() {
return (
<BillboardY position={[0, 2, 0]}>
<mesh>
<planeGeometry args={[2, 0.5]} />
<meshStandardMaterial color="#333" />
</mesh>
</BillboardY>
)
}

Props

<group> と同じ Props を受け取ります(position, rotation, scale など)。

フックとして使う

useBillboardY フックを使うと、任意の Object3D に対してY軸ビルボードを適用できます。詳しくはuseBillboardYを参照してください。


フック

useInstanceState

インスタンス内の全ユーザー間で状態を同期します。React の useState と同じインターフェースです。

import { useInstanceState } from '@xrift/world-components';

function Counter() {
const [count, setCount] = useInstanceState('counter', 0);

return (
<mesh onClick={() => setCount(count + 1)}>
{/* count は全ユーザーで同期される */}
</mesh>
);
}

引数

引数TypeDescription
keystring状態の一意な識別子
initialValueT初期値

戻り値

[value: T, setValue: (newValue: T) => void] - useState と同じ形式


useInstanceEvent

インスタンスイベントの送受信を行うフックです。プラットフォームイベント(user-joined, user-left)の受信や、ワールド独自のカスタムイベントの送受信ができます。

import { useInstanceEvent } from '@xrift/world-components';

// プラットフォームイベントの受信(受信のみ、emit 不可)
useInstanceEvent('user-joined', (data) => {
console.log('User joined:', data)
})

// カスタムイベントの送受信
const emitReaction = useInstanceEvent('reaction', (data) => {
console.log('Reaction received:', data)
})
emitReaction({ emoji: '👍', userId: 'user-1' })

引数

引数TypeDescription
eventNamestringイベント名
callback(data: T) => voidイベント受信時のコールバック

戻り値

(data: T) => void - イベント送信関数。プラットフォーム予約イベント(user-joined, user-left)の場合は no-op になります。

イベントの種類

種類イベント名送信受信説明
プラットフォームuser-joined-ユーザーがインスタンスに入室
プラットフォームuser-left-ユーザーがインスタンスから退室
カスタム任意の文字列ワールド独自のイベント

ユースケース

リアクション機能
import { useInstanceEvent } from '@xrift/world-components';
import { useCallback, useState } from 'react';

function ReactionSystem() {
const [reactions, setReactions] = useState<{ emoji: string }[]>([]);

const emitReaction = useInstanceEvent('reaction', (data: { emoji: string }) => {
setReactions(prev => [...prev, data]);
});

const sendReaction = useCallback((emoji: string) => {
emitReaction({ emoji });
}, [emitReaction]);

return (
<mesh onClick={() => sendReaction('👍')}>
<boxGeometry args={[1, 1, 0.2]} />
<meshStandardMaterial color="yellow" />
</mesh>
);
}
入退室の検知
import { useInstanceEvent } from '@xrift/world-components';

function JoinLeaveNotifier() {
useInstanceEvent('user-joined', (data) => {
console.log('User joined:', data);
});

useInstanceEvent('user-left', (data) => {
console.log('User left:', data);
});

return null;
}
カスタムイベントとインスタンスステートの使い分け
  • useInstanceEvent: 一時的なイベント通知(リアクション、エフェクトトリガーなど)に適しています。
  • useInstanceState: 永続的な同期状態(カウンター、ON/OFF状態など)に適しています。
開発環境での動作

開発環境ではローカル EventEmitter が使用されるため、同一ブラウザ内でのみイベントが送受信されます。本番環境ではプラットフォームが WebSocket 実装を注入し、インスタンス内の全ユーザー間でイベントが共有されます。


useScreenShareContext

画面共有の状態を取得するフックです。

import { useScreenShareContext } from '@xrift/world-components';

function MyComponent() {
const { videoElement, isSharing, startScreenShare, stopScreenShare } = useScreenShareContext();

return (
<button onClick={isSharing ? stopScreenShare : startScreenShare}>
{isSharing ? '共有を停止' : '共有を開始'}
</button>
);
}

戻り値

PropertyTypeDescription
videoElementHTMLVideoElement | null表示する映像のvideo要素
isSharingboolean自分が共有中かどうか
startScreenShare() => void共有開始
stopScreenShare() => void共有停止

useSpawnPoint

プラットフォーム側がスポーン地点情報を取得するためのフックです。

import { useSpawnPoint } from '@xrift/world-components';

function MyPlatform() {
const spawnPoint = useSpawnPoint();
// spawnPoint: { position: [x, y, z], yaw: number }
}

戻り値

PropertyTypeDescription
position[number, number, number]スポーン位置
yawnumberスポーン時の向き(度数法)
使用先

このフックは xrift-frontend(プラットフォーム)側での使用を想定しています。ワールド開発者は SpawnPoint コンポーネントを使用してください。


useUsers

ワールドに参加しているユーザー情報と位置情報を取得するフックです。自分自身(ローカルユーザー)と他の参加者(リモートユーザー)の情報にアクセスできます。

import { useUsers } from '@xrift/world-components';

function ParticipantCount() {
const { localUser, remoteUsers, getMovement, getLocalMovement } = useUsers();

const totalCount = (localUser ? 1 : 0) + remoteUsers.length;

return (
<div>
<p>参加者数: {totalCount}</p>
</div>
);
}

戻り値

PropertyTypeDescription
localUserUser | null自分自身のユーザー情報
remoteUsersUser[]他の参加者のユーザー情報の配列
getMovement(id: string) => PlayerMovement | undefined指定ユーザーの位置情報を取得
getLocalMovement() => PlayerMovement自分の位置情報を取得

User 型

interface User {
id: string; // ユーザーID
displayName: string; // 表示名
userIconUrl: string | null; // アバターアイコンURL
isGuest: boolean; // ゲストかどうか
}

PlayerMovement 型

interface PlayerMovement {
position: { x: number; y: number; z: number };
direction: { x: number; z: number };
horizontalSpeed: number;
verticalSpeed: number;
rotation: { yaw: number; pitch: number };
isGrounded: boolean;
isJumping: boolean;
isInVR?: boolean;
vrTracking?: VRTrackingData;
}

useFrame 内での位置情報取得

getMovement()getLocalMovement()useFrame 内で毎フレーム呼び出すことができます。これらの関数は再レンダリングを発生させずに最新の位置情報を取得できます。

import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Group } from 'three';

function FollowCamera() {
const groupRef = useRef<Group>(null);
const { getLocalMovement } = useUsers();

useFrame(() => {
const movement = getLocalMovement();
if (!groupRef.current) return;

// 自分の位置の少し上にオブジェクトを配置
groupRef.current.position.set(
movement.position.x,
movement.position.y + 3,
movement.position.z
);
});

return (
<group ref={groupRef}>
<pointLight intensity={1} />
</group>
);
}

ユースケース

ユーザーの頭上にHUDを表示
import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Group } from 'three';
import { Text } from '@react-three/drei';

function UserHUD({ user, getMovement }) {
const groupRef = useRef<Group>(null);

useFrame(() => {
const movement = getMovement(user.id);
if (!movement || !groupRef.current) return;

// ユーザーの頭の上に配置
groupRef.current.position.set(
movement.position.x,
movement.position.y + 2,
movement.position.z
);
});

return (
<group ref={groupRef}>
<Text fontSize={0.2}>{user.displayName}</Text>
</group>
);
}

function UserHUDs() {
const { remoteUsers, getMovement } = useUsers();

return (
<>
{remoteUsers.map(user => (
<UserHUD key={user.id} user={user} getMovement={getMovement} />
))}
</>
);
}
近くにいるユーザーを検出
import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useState } from 'react';

function ProximityDetector() {
const { remoteUsers, getMovement, getLocalMovement } = useUsers();
const [nearbyUsers, setNearbyUsers] = useState<string[]>([]);

useFrame(() => {
const myPos = getLocalMovement().position;
const nearby: string[] = [];

remoteUsers.forEach(user => {
const movement = getMovement(user.id);
if (!movement) return;

const distance = Math.sqrt(
Math.pow(myPos.x - movement.position.x, 2) +
Math.pow(myPos.y - movement.position.y, 2) +
Math.pow(myPos.z - movement.position.z, 2)
);

if (distance < 5) {
nearby.push(user.displayName);
}
});

// 配列の内容が変わった場合のみ更新
if (JSON.stringify(nearby) !== JSON.stringify(nearbyUsers)) {
setNearbyUsers(nearby);
}
});

return null;
}
ユーザー間の距離を計算
import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Line } from '@react-three/drei';

function DistanceLine({ targetUser, getMovement, getLocalMovement }) {
const lineRef = useRef<any>(null);

useFrame(() => {
const myPos = getLocalMovement().position;
const targetMovement = getMovement(targetUser.id);
if (!targetMovement || !lineRef.current) return;

lineRef.current.geometry.setPositions([
myPos.x, myPos.y + 1, myPos.z,
targetMovement.position.x, targetMovement.position.y + 1, targetMovement.position.z
]);
});

return (
<Line
ref={lineRef}
points={[[0, 0, 0], [0, 0, 0]]}
color="yellow"
lineWidth={2}
/>
);
}
パフォーマンスのヒント

getMovement()getLocalMovement()useFrame 内で毎フレーム呼び出しても問題ありません。これらは内部的にキャッシュされた値を返すため、パフォーマンスへの影響は最小限です。

remoteUsers の更新タイミング

remoteUsers 配列はユーザーの参加/離脱時のみ更新されます。ユーザーの位置情報の変化では再レンダリングは発生しません。位置情報は常に getMovement() を使用して取得してください。


useTeleport

自分自身のアバターを指定した座標に瞬間移動させるフックです。ポータル、エレベーター、ワープゾーンなどのユースケースに対応します。

import { useTeleport } from '@xrift/world-components';

function MyComponent() {
const { teleport } = useTeleport();

const handleTeleport = useCallback(() => {
teleport({ position: [50, 0, 30], yaw: 180 });
}, [teleport]);
}

API

interface TeleportDestination {
position: [number, number, number]
yaw?: number // 度数法(0-360)省略時は現在の向きを維持
}

const { teleport } = useTeleport()

パラメータ(TeleportDestination)

パラメータ必須説明
position[number, number, number]Yesテレポート先の座標 [x, y, z]
yawnumberNoテレポート後の向き(度数法 0-360)。省略時は現在の向きを維持

使用例

ポータルでテレポート
import { useTeleport, Interactable } from '@xrift/world-components'
import { useCallback } from 'react'

function MyWorld() {
const { teleport } = useTeleport()

const handlePortal = useCallback(() => {
teleport({ position: [50, 0, 30], yaw: 180 })
}, [teleport])

return (
<Interactable id="portal" onInteract={handlePortal}>
<mesh>
<torusGeometry />
<meshStandardMaterial color="purple" />
</mesh>
</Interactable>
)
}
yaw の省略

yaw を省略するとテレポート後もプレイヤーの現在の向きが維持されます。特定の方向を向かせたい場合のみ指定してください。


useConfirm

ユーザーに確認モーダルを表示するフックです。ワールド移動など重要なアクションの前に確認を求めることができます。

import { useConfirm } from '@xrift/world-components';

function MyComponent() {
const { requestConfirm } = useConfirm();

const handleAction = async () => {
const ok = await requestConfirm({ message: 'ワールドを移動しますか?' });
if (ok) {
// 確認された場合の処理
}
};
}

戻り値

PropertyTypeDescription
requestConfirm(options: ConfirmOptions) => Promise<boolean>確認モーダルを表示し、結果を返す

ConfirmOptions

PropertyType必須Description
messagestringYesユーザーに表示するメッセージ
titlestringNoダイアログのタイトル
confirmLabelstringNo確認ボタンのラベル
cancelLabelstringNoキャンセルボタンのラベル

使用例

外部サイトへの遷移前に確認
import { useConfirm, Interactable } from '@xrift/world-components'

function ExternalLink() {
const { requestConfirm } = useConfirm()

const handleClick = async () => {
const ok = await requestConfirm({
title: '外部サイトへ移動',
message: '外部サイトへ移動します。よろしいですか?',
confirmLabel: '移動する',
cancelLabel: 'キャンセル',
})
if (ok) {
window.open('https://example.com', '_blank')
}
}

return (
<Interactable id="external-link" onInteract={handleClick}>
<mesh>
<boxGeometry args={[1, 1, 0.2]} />
<meshStandardMaterial color="cyan" />
</mesh>
</Interactable>
)
}
iOS Safari のポップアップブロック回避

iPhone 等のモバイルブラウザでは、ユーザー操作を起点としない window.open や外部サイトへの遷移がブロックされます。useConfirm で確認ダイアログを挟むことで、ユーザー操作起点のイベントチェーンを作り、ブラウザのポップアップブロックを回避できます。

Portal コンポーネントとの関係

Portal コンポーネントは内部で useInstance フックを経由して useConfirm を使用しています。Portal を使う場合は useConfirm を直接呼ぶ必要はありません。


useInstance

インスタンス情報の取得と確認付き遷移を提供するフックです。内部で useConfirm を使い、遷移前に確認モーダルを表示します。

import { useInstance } from '@xrift/world-components'

function MyComponent() {
const { info, navigateWithConfirm } = useInstance('target-instance-id')

if (!info) return null

return (
<mesh onClick={navigateWithConfirm}>
{/* インスタンス名: {info.name} */}
</mesh>
)
}

引数

引数TypeDescription
instanceIdstring取得するインスタンスのID

戻り値

PropertyTypeDescription
infoInstanceInfo | nullインスタンス情報(取得前は null)
navigateWithConfirm() => Promise<void>確認モーダル付きでインスタンスへ遷移

InstanceInfo 型

フィールドTypeDescription
idstringインスタンスID
namestringインスタンス名
descriptionstring | null説明
currentUsersnumber現在のユーザー数
maxCapacitynumber最大収容人数
isPublicboolean公開かどうか
allowGuestsbooleanゲスト許可
owner{ id, displayName, userIconUrl? }オーナー情報(任意)
worldWorldInfo所属ワールドの情報

useWorld

ワールド情報の取得を提供するフックです。

import { useWorld } from '@xrift/world-components'

function MyComponent() {
const { info } = useWorld('target-world-id')

if (!info) return null

return (
<mesh>
{/* ワールド名: {info.name} */}
</mesh>
)
}

引数

引数TypeDescription
worldIdstring取得するワールドのID

戻り値

PropertyTypeDescription
infoWorldInfo | nullワールド情報(取得前は null)

WorldInfo 型

フィールドTypeDescription
idstringワールドID
namestringワールド名
descriptionstring | null説明
thumbnailUrlstring | nullサムネイルURL
isPublicboolean公開かどうか
instanceCountnumberインスタンス数
totalVisitCountnumber総訪問数
uniqueVisitorCountnumberユニーク訪問者数
favoriteCountnumberお気に入り数
owner{ id, displayName, userIconUrl? }オーナー情報(任意)
permissions{ allowedDomains: string[], allowedCodeRules: string[] } | undefinedワールドが必要とする権限(詳細

useBillboardY

対象の Object3D を毎フレームカメラに向けてY軸のみ回転させるフックです。BillboardY コンポーネントの内部で使用されていますが、任意の Object3D に適用したい場合に直接使えます。

内部では sentinel Mesh の onBeforeRender を使用しているため、Mirror(Reflector)の virtualCamera でも正しい回転が適用されます。

import { useBillboardY } from '@xrift/world-components'
import type { Group } from 'three'

function CustomBillboard() {
const ref = useBillboardY<Group>()

return (
<group ref={ref}>
<mesh>
<planeGeometry args={[1, 1.5]} />
<meshBasicMaterial map={fireTexture} />
</mesh>
</group>
)
}

戻り値

PropertyTypeDescription
refRefObject<T>対象の Object3D にアタッチする ref

関連

  • BillboardY コンポーネント — このフックを <group> でラップしたもの
  • getBillboardYRotation(cameraWorldPos, targetWorldPos) — 回転角度を計算する純粋関数(InstancedMesh などで手動計算する場合に使用)

定数

LAYERS

Three.js のレイヤーシステムを活用した定数です。カメラやRaycasterのレイヤー設定に使用します。

import { LAYERS } from '@xrift/world-components';
定数名説明
LAYERS.DEFAULT0デフォルトレイヤー(すべてのオブジェクトが初期状態で属する)
LAYERS.FIRST_PERSON_ONLY9一人称視点のみ表示(VRMFirstPerson用)
LAYERS.THIRD_PERSON_ONLY10三人称視点のみ表示(VRMFirstPerson用)
LAYERS.INTERACTABLE11インタラクト可能オブジェクト(Raycast対象)

関連する型

type LayerName = 'DEFAULT' | 'FIRST_PERSON_ONLY' | 'THIRD_PERSON_ONLY' | 'INTERACTABLE';
type LayerNumber = 0 | 9 | 10 | 11;

ユースケース

  • Raycasterでインタラクション対象を検出する際のレイヤー設定
  • VRモードでの一人称/三人称の表示切り替え