跳到主要内容

世界空间界面

世界空间界面位于场景中,例如墙上标牌、机器控制面板、角色姓名或 XR 菜单。它使用与 HUD 相同的元素,放到世界空间屏幕上,由场景摄像机像普通对象一样观察。

世界空间界面

世界空间屏幕​

世界空间屏幕由实体变换定位,resolution 表示局部单位下的尺寸,并以实体为中心:

// A 400 × 240 unit panel, scaled to 1.6 × 0.96 meters, two meters up
const sign = new pc.Entity('sign');
sign.addComponent('screen', {
screenSpace: false,
resolution: [400, 240]
});
sign.setLocalScale(0.004, 0.004, 0.004);
sign.setPosition(0, 2, -3);
app.root.addChild(sign);

const background = new pc.Entity('background');
background.addComponent('element', {
type: pc.ELEMENTTYPE_IMAGE,
anchor: [0, 0, 1, 1],
margin: [0, 0, 0, 0],
color: new pc.Color(0.16, 0.18, 0.23)
});
sign.addChild(background);

const title = new pc.Entity('title');
title.addComponent('element', {
type: pc.ELEMENTTYPE_TEXT,
fontAsset: font.id,
text: 'Exit',
fontSize: 96,
anchor: [0.5, 0.5, 0.5, 0.5],
pivot: [0.5, 0.5]
});
sign.addChild(title);

尺寸与缩放​

米制尺寸等于分辨率乘实体缩放。像二维界面一样按布局选择分辨率,再通过缩放控制世界中的大小:

分辨率缩放米制尺寸字号 32 的文本
640 × 3200.016.4 × 3.2高 32 厘米
400 × 2400.0041.6 × 0.96高 12.8 厘米
1000 × 6000.0011 × 0.6高 3.2 厘米

世界空间屏幕没有缩放模式,元素的世界尺寸固定,观察距离越远,看起来越小。

背面观察​

世界空间屏幕是单面的,背面既不绘制,也不接收输入。双面标牌可将两个屏幕背靠背摆放,第二个绕 Y 轴旋转 180 度。

深度与绘制顺序​

世界空间元素默认位于最后绘制的 UI 图层,通常会与场景深度测试,因此被墙遮挡。但使用 CameraFrame 时,UI 在后处理后绘制,没有场景深度,就会穿墙。将其纳入场景的方法详见世界空间界面与后处理。

面向摄像机​

姓名牌等始终朝向观察者的面板,可以每帧复制摄像机旋转,保持与视图平行,避免斜看文字:

app.on('update', () => {
nameTag.setRotation(camera.getRotation());
});

角色上方标签​

三维角色头顶的血条,不同距离下保持相同屏幕尺寸

角色标签、血条和标记可用两种方式:

作为角色子项的世界空间屏幕每帧移到角色投影位置的屏幕空间元素
屏幕尺寸越远越小始终相同
被墙遮挡是,除非使用 CameraFrame否,覆盖场景
面向摄像机需要额外处理始终如此

世界空间屏幕作为角色子项放在头顶,会随角色移动。屏幕空间方式则先通过摄像机 worldToScreen 取得画布位置,再转换为屏幕单位:

// The label is on a screen-space screen, anchored to its bottom-left corner: anchor 0, 0, 0, 0
const head = new pc.Vec3();
const onCanvas = new pc.Vec3();
const inView = new pc.Vec3();
const overHead = new pc.Vec3(0, 2.2, 0);

app.on('update', () => {
head.add2(character.getPosition(), overHead);
// CSS pixels from the canvas's top-left corner
camera.camera.worldToScreen(head, onCanvas);

// Hide the label when the character is behind the camera, which its depth in view space
// tells: a point in front of the camera has a negative z
camera.camera.viewMatrix.transformPoint(head, inView);
label.enabled = inView.z < 0;

// One CSS pixel is canvas.width / canvas.clientWidth pixels of the drawing buffer, and one
// screen unit is screen.scale of them. The screen's y axis points up, the canvas's down
const canvas = app.graphicsDevice.canvas;
const units = (canvas.width / canvas.clientWidth) / screen.screen.scale;
label.setLocalPosition(onCanvas.x * units, (canvas.clientHeight - onCanvas.y) * units, 0);
});

将标签轴心设为底边中央 0.5, 0,使其显示在投影点上方。大量标签应集中到一个 update 更新,并跳过过远或屏幕外角色。

也可以使用 HTML 标签,直接按 CSS 像素定位,无需单位转换,详见 HTML 与 CSS。

世界坐标转屏幕坐标

交互​

启用输入后,世界空间元素与屏幕空间元素接收相同的鼠标、触摸和 XR 控制器事件。摄像机必须渲染其图层。屏幕空间优先检测;世界空间屏幕重叠时,优先级较高者接收事件,与距离无关,详见命中检测。

另请参阅​