XR
PlayCanvas React 可通过 WebXR 为应用添加虚拟现实和增强现实。
基础配置
需要以下内容:
- XR 脚本:从引擎包导入控制器和导航。
- 摄像机配置:将脚本挂到摄像机 rig。
- 界面控制:通过用户交互进入和退出会话。
- 安全上下文:使用 HTTPS,开发可用 localhost。
XR 脚本
引擎提供两项基础脚本:
- XrControllers:下载并显示检测到的控制器模型,包括手部追踪。
- XrNavigation:通过指向和选择实现传送。
从脚本包导入:
import { XrControllers } from 'playcanvas/scripts/esm/xr/xr-controllers.mjs'
import { XrNavigation } from 'playcanvas/scripts/esm/xr/xr-navigation.mjs'
摄像机配置
推荐将脚本挂到摄像机父实体:
<Entity name="camera-root">
<Entity name="camera" position={[0, 1.6, 0]}>
<Camera clearColor="#1e1e1e" />
</Entity>
<Script script={XrControllers} />
<Script script={XrNavigation} />
</Entity>
摄像机位于约 1.6 米眼高,脚本位于父项。
启动会话
WebXR 需要用户手势,可通过 app.xr 启动和管理:
const app = useApp()
const startVR = () => {
const camera = app.root.findComponent('camera')
if (camera) {
app.xr.start(camera, 'immersive-vr', 'local-floor')
}
}
const startAR = () => {
const camera = app.root.findComponent('camera')
if (camera) {
app.xr.start(camera, 'immersive-ar', 'local-floor')
}
}
完整示例
以下包含 AR/VR 按钮和可导航的立方体场景:
- 演示
- 代码
xr-example.jsx
// ↑ imports hidden
export const XrExample = () => {
const app = useApp()
const [xrActive, setXrActive] = useState(false)
const arAvailable = useMemo(() => app?.xr?.isAvailable('immersive-ar'), [app.xr])
const vrAvailable = useMemo(() => app?.xr?.isAvailable('immersive-vr'), [app.xr])
// Listen for XR session start/end
useEffect(() => {
const onStart = () => setXrActive(true)
const onEnd = () => setXrActive(false)
app.xr?.on('start', onStart)
app.xr?.on('end', onEnd)
return () => {
app.xr?.off('start', onStart)
app.xr?.off('end', onEnd)
}
}, [app])
const startAR = () => {
const camera = app.root.findComponent('camera')
if (camera) {
app.xr.start(camera, 'immersive-ar', 'local-floor')
}
}
const startVR = () => {
const camera = app.root.findComponent('camera')
if (camera) {
app.xr.start(camera, 'immersive-vr', 'local-floor')
}
}
const endXR = () => app.xr.end()
return (
<>
{/* XR UI Controls */}
<div className="overlay">
{ !xrActive && arAvailable && <button onClick={startAR}>Enter AR</button> }
{ !xrActive && vrAvailable && <button onClick={startVR}>Enter VR</button> }
{ xrActive && <button data-selected onClick={endXR}>Exit XR</button> }
</div>
{/* Camera with XR support */}
<Entity name="camera-root">
<Entity name="camera" position={[4, 1, 4]} rotation={[0, 45, 0]}>
<Camera clearColor="#1e1e1e" />
{ !xrActive && <Script script={CameraControls} enableFly={false} pitchRange={new Vec2(-90, -5)} /> }
</Entity>
<Script script={XrControllers} />
<Script script={XrNavigation} />
</Entity>
{/* Scene content - a grid of cubes */}
<Entity name="cube-1" position={[-2, 0, 0]}>
<Render type="box" />
</Entity>
<Entity name="cube-2" position={[0, 0, 0]}>
<Render type="box" />
</Entity>
<Entity name="cube-3" position={[2, 0, -2]}>
<Render type="box" />
</Entity>
</>
)
}
提示
- 按 Escape 退出会话。
- 可用性取决于设备和浏览器。
- 完整测试需 VR 头显或支持 AR 的手机。
- 开发时可在 Chrome/Edge 开发者工具配合 WebXR 模拟。
检查可用性
检查当前是否支持 AR 或 VR:
const app = useApp()
const arAvailable = app.xr.isAvailable('immersive-ar')
const vrAvailable = app.xr.isAvailable('immersive-vr')
XR 事件
监听会话事件更新界面:
useEffect(() => {
const onStart = () => console.log('XR session started')
const onEnd = () => console.log('XR session ended')
app.xr.on('start', onStart)
app.xr.on('end', onEnd)
return () => {
app.xr.off('start', onStart)
app.xr.off('end', onEnd)
}
}, [app])
后续步骤
更多高级能力详见: