XRift ワールド向けのラジアルメニューコンポーネントです。
キーを押している間、カメラ正面に円形のボタンメニューを表示します。
ContextMenu/ フォルダをあなたのワールドプロジェクトの src/components/ にコピーしてください。
git clone https://github.com/N-JELLY/XRift_ContextMenu.git
cp -r XRift_ContextMenu/ContextMenu your-world/src/components/import { ContextMenu, MenuButtonConfig } from './components/ContextMenu'プロジェクトの隣にクローンして、パスで直接 import する方法もあります。
# your-world/ の隣にクローン
git clone https://github.com/N-JELLY/XRift_ContextMenu.git// src/World.tsx から参照する例(相対パスはプロジェクト構成に合わせて調整)
import { ContextMenu, MenuButtonConfig } from '../../XRift_ContextMenu/ContextMenu'TypeScript の paths エイリアスを使うとすっきり書けます(tsconfig.json):
{
"compilerOptions": {
"paths": {
"@context-menu": ["../XRift_ContextMenu/ContextMenu/index.tsx"]
}
}
}import { ContextMenu, MenuButtonConfig } from '@context-menu'ContextMenu コンポーネントに buttons 配列を渡します。デフォルトで R キー を押している間メニューが表示されます。
import { ContextMenu, MenuButtonConfig } from './components/ContextMenu'
const buttons: MenuButtonConfig[] = [
{ icon: '●', label: 'Sphere', onSelect: () => console.log('sphere') },
{ icon: '■', label: 'Box', onSelect: () => console.log('box') },
{ icon: '⚙', label: 'Setting', onSelect: () => console.log('setting') },
]
export const World = () => (
<>
<ContextMenu buttons={buttons} />
</>
)ref を渡すとメニューの表示位置(Group)を取得でき、スポーン位置をメニューに合わせられます。
import { useRef, useState } from 'react'
import { Group } from 'three'
import { ContextMenu, MenuButtonConfig } from './components/ContextMenu'
interface SpawnedObject {
id: number
type: 'sphere' | 'box'
position: [number, number, number]
}
let spawnIdCounter = 0
export const World = () => {
const menuRef = useRef<Group>(null)
const [spawnedObjects, setSpawnedObjects] = useState<SpawnedObject[]>([])
const spawnAt = (type: SpawnedObject['type']) => {
const pos = menuRef.current?.position
if (!pos) return
setSpawnedObjects(prev => [...prev, { id: spawnIdCounter++, type, position: [pos.x, pos.y, pos.z] }])
}
const buttons: MenuButtonConfig[] = [
{ icon: '●', label: 'Sphere', onSelect: () => spawnAt('sphere') },
{ icon: '■', label: 'Box', onSelect: () => spawnAt('box') },
{ icon: '🗑️', label: 'Delete', onSelect: () => setSpawnedObjects([]) },
]
return (
<>
<ContextMenu ref={menuRef} buttons={buttons} />
{spawnedObjects.map(obj => (
<mesh key={obj.id} position={obj.position}>
{obj.type === 'sphere' ? <sphereGeometry args={[0.3, 32, 32]} /> : <boxGeometry args={[0.5, 0.5, 0.5]} />}
<meshStandardMaterial color={obj.type === 'sphere' ? '#6688ff' : '#ff8844'} />
</mesh>
))}
</>
)
}| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
buttons |
MenuButtonConfig[] |
必須 | 表示するボタンの配列 |
triggerKey |
string |
'r' |
メニューを開くキー |
distance |
number |
1.4 |
カメラからメニューまでの距離(m) |
ref |
React.Ref<Group> |
— | メニューの Group への ref(スポーン位置の取得などに使用) |
| フィールド | 型 | デフォルト | 説明 |
|---|---|---|---|
icon |
string |
— | ボタン中央に表示するアイコン(絵文字可) |
label |
string |
— | ボタン下のラベル |
color |
string |
'#646464' |
通常時の色 |
colorHover |
string |
'#a0a0ff' |
ホバー時の色 |
colorClick |
string |
'#ffffff' |
クリック時の色 |
onSelect |
() => void |
必須 | 選択時のコールバック |
| 操作 | 動作 |
|---|---|
R キーを押す |
メニューを表示(カメラ正面に固定) |
R キーを離す |
メニューを非表示 |
| ボタンをクリック | onSelect を実行 |
MIT