XR 界面
沉浸式会话没有可承载 HUD 的普通平面屏幕,用户通过头显观察场景,或在 AR 中透过手机摄像头观察。XR 界面应采用世界空间屏幕,即场景中可用控制器或手指向的面板。屏幕空间界面适用于平面显示器,不应用于 XR。
构建面板
像普通世界空间屏幕一样构建,并按米控制大小。缩放为 0.001 时,一个单位为一毫米,800 × 500 屏幕宽 80 厘米,字号 32 的文本高 3.2 厘米。应在头显中检查,因为显示器上易读的文字在 XR 中可能过小。
面板挂载位置决定其移动方式:
- 世界中。 固定于房间的面板,例如桌上控制面板,通常最舒适。距离用户约一臂到两米,略低于视线,并朝向用户。
- 手或控制器上。 作为控制器实体子项时,会随控制器移动,例如腕部菜单。XrMenu 支持手和控制器。
- 跟随视图。 刚性固定在摄像机上的面板始终占据相同视野位置,容易让人不适。宜缓慢跟随视图,类似 XrMenu 始终显示模式。
指向与选择
XR 输入通过 app.xr 管理。编辑器、React 和 Web Components 会自动创建;pc.AppBase 应用则需要在 AppOptions 中指定 options.xr = pc.XrManager。
会话期间,ElementInput 每帧将各 XR 输入源,即控制器或手的射线,投向启用输入的元素,并触发:
| 事件 | 触发时机 |
|---|---|
selectenter, selectleave | 射线进入或离开元素 |
selectstart | 射线指向元素时开始选择,例如按扳机或捏合 |
selectmove | 在元素上开始的选择持续期间,每帧触发 |
selectend | 选择结束 |
click | 选择在同一元素上开始并结束 |
因此,按钮在射线指向时悬停,选择保持时按下,选择在其上结束时点击,与鼠标类似。click 监听器无需为 XR 修改。各输入源的选择动作详见输入源。
输入源的两个属性控制其界面交互:
app.xr.input.on('add', (inputSource) => {
// Let only the right-hand controller or hand point at the interface
if (inputSource.handedness !== pc.XRHAND_RIGHT) {
inputSource.elementInput = false;
}
});
app.on('update', () => {
for (const inputSource of app.xr.input.inputSources) {
// The entity of the element that this input source points at, or null
const target = inputSource.elementEntity;
if (target) {
// for example, shorten the drawn laser to end at the panel
}
}
});
要让全部元素忽略 XR 输入,创建 ElementInput 时设置 useXr: false。
手部追踪
每只手是独立输入源,带自己的射线,拇指与食指捏合表示选择。元素和按钮像响应控制器一样响应手,详见手部追踪。
XR 菜单
引擎 XrMenu 根据列表构建按钮菜单,可显示在朝向用户的张开手掌上、按键后的控制器上,或始终缓慢跟随视图。选中条目会触发应用事件。它使用自身射线和指尖检测,既支持控制器射线,也支持手指戳按。
- 引擎
- 编辑器
- React
- Web Components
import { XrMenu } from 'playcanvas/scripts/esm/xr/xr-menu.mjs';
const menu = new pc.Entity('menu');
menu.addComponent('script');
menu.script.create(XrMenu, {
properties: {
menuItems: [
{ label: 'Restart', eventName: 'menu:restart' },
{ label: 'Exit', eventName: 'xr:end' }
],
fontAsset: font
}
});
app.root.addChild(menu);
app.on('menu:restart', () => {
restartLevel();
});
从引擎仓库的 scripts/esm/xr 添加 xr-menu.mjs 为脚本资源。在实体 Script 组件中挂载 xrMenu,填写 Menu Items 和 Font Asset,再在自己的脚本中监听条目事件。
import { XrMenu } from 'playcanvas/scripts/esm/xr/xr-menu.mjs';
import { Entity } from '@playcanvas/react';
import { Script } from '@playcanvas/react/components';
export function Menu({ font }) {
return (
<Entity name="menu">
<Script script={XrMenu} fontAsset={font}
menuItems={[{ label: 'Restart', eventName: 'menu:restart' }, { label: 'Exit', eventName: 'xr:end' }]} />
</Entity>
);
}
<pc-asset src="https://cdn.jsdelivr.net/npm/playcanvas@latest/scripts/esm/xr/xr-menu.mjs"></pc-asset>
<pc-entity name="menu">
<pc-script>
<pc-script-instance name="xrMenu" attributes='{
"menuItems": [
{ "label": "Restart", "eventName": "menu:restart" },
{ "label": "Exit", "eventName": "xr:end" }
],
"fontAsset": "asset:arial"
}'></pc-script-instance>
</pc-script>
</pc-entity>
在 <pc-app> 中声明 <pc-asset> 及字体资源。从 npm 或 CDN 加载引擎 XR 脚本的方法详见 XR 脚本。
只有 label、没有 eventName 的条目显示为普通文字,而非按钮。setItemLabel() 可在菜单显示时修改文本。示例的 xr:end 由场景中的 XrSession 脚本处理并结束会话,详见 XR 脚本。菜单显隐时,XrMenu 还会触发 xr:menu:active。
舒适性
- 保持面板稳定,或让它缓慢跟随,不要刚性固定到视图。
- 将常用控件放在容易看到和够到的位置,避免用户伸展或转身。
- 使用较大按钮,小按钮可添加命中扩展,因为射线精确指向比鼠标更困难。
- 明确显示射线所指对象,例如使用按钮悬停染色。
进入 XR
浏览器仅允许点击或触摸等用户操作启动沉浸式会话。引擎示例通过画布上方 HTML 按钮启动,开始和结束方法详见使用 WebXR。