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

ワールド空間UI

ワールド空間のインターフェースは、シーンの中に置かれます。壁の標識、機械の操作パネル、キャラクターの上のネームタグ、XRのメニューなどです。HUDと同じエレメントで作られ、シーンのカメラから他のオブジェクトと同じように見えるワールド空間のスクリーン上に配置されます。

World UI

ワールド空間のスクリーン​

ワールド空間のスクリーンは、そのエンティティのトランスフォームによって配置されます。resolutionはエンティティのローカル単位でのサイズで、スクリーンはエンティティを中心に配置されます。

// 400 × 240単位のパネルを1.6 × 0.96メートルにスケールし、2メートルの高さに置く
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);

サイズとスケール​

ワールド空間のスクリーンのメートル単位のサイズは、解像度にエンティティのスケールを掛けたものです。解像度は2Dのインターフェースと同じようにレイアウトに合わせて選び、スケールはワールドでのサイズに合わせて選びます。

解像度スケールメートル単位のサイズフォントサイズ32のテキスト
640 × 3200.016.4 × 3.2高さ32 cm
400 × 2400.0041.6 × 0.96高さ12.8 cm
1000 × 6000.0011 × 0.6高さ3.2 cm

ワールド空間のスクリーンにはスケールモードがありません。エレメントはワールド内で同じサイズのままなので、遠くから見るほど小さく見えます。

裏側から見た場合​

ワールド空間のスクリーンは片面です。裏側からは描画されず、エレメントは入力を受け取りません。両側から読む標識には、2つのスクリーンを背中合わせに置き、2つ目をy軸を中心に180度回転させます。

深度と描画順​

ワールド空間のエレメントはデフォルトでUIレイヤーにあり、このレイヤーはシーンの他の部分の後に描画されます。エレメントはシーンに対して深度テストされるため壁に隠れますが、カメラがCameraFrameを通してレンダリングする場合は例外です。その場合、UIレイヤーはポストプロセスの後にシーンの深度なしで描画されるため、ワールド空間のエレメントが壁越しに見えてしまいます。代わりにシーンの一部として描画する方法は、ワールド空間UIとポストプロセスを参照してください。

カメラの方を向ける​

ネームタグのように常に見る人の方を向くべきパネルは、毎フレームカメラの回転をコピーできます。これによりパネルは常にビューと平行になり、テキストが斜めから見えることはありません。

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

キャラクターの上のラベル​

3Dシーン内の複数のキャラクターと、それぞれの頭上に浮かぶヘルスバー。バーは、キャラクターまでの距離に関係なく画面上で同じサイズを保ちます

キャラクターの上のラベル、ヘルスバー、マーカーは、2つの方法で描画できます。

キャラクターの子としてのワールド空間のスクリーン毎フレームキャラクターの位置に移動するスクリーン空間のエレメント
画面上のサイズ遠いほど小さい常に同じ
壁に隠れるはい(カメラがCameraFrameを使う場合を除く)いいえ(シーンの上に描画される)
カメラの方を向くカメラの方を向けるを使った場合のみ常に向く

ワールド空間のスクリーンは、キャラクターのエンティティの子として頭上に配置され、キャラクターと一緒に動きます。代わりにスクリーン空間のエレメントを配置するには、カメラのworldToScreenでキャラクターがキャンバス上のどこにあるかを求め、それをスクリーンの単位に変換します。

// ラベルはスクリーン空間のスクリーン上にあり、その左下隅にアンカーされている(アンカーは0, 0, 0, 0)
const head = new pc.Vec3();
const onCanvas = new pc.Vec3();
const overHead = new pc.Vec3(0, 2.2, 0);

app.on('update', () => {
head.add2(character.getPosition(), overHead);
// キャンバスの左上隅からのCSSピクセル位置と、カメラの前方への距離
camera.camera.worldToScreen(head, onCanvas);

// キャラクターがカメラの後ろにあるときはラベルを隠す
label.enabled = onCanvas.z > 0;

// 1 CSSピクセルは描画バッファのcanvas.width / canvas.clientWidthピクセルで、1スクリーン単位は
// 描画バッファのscreen.scaleピクセル。スクリーンのy軸は上向き、キャンバスのy軸は下向き
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)にします。ラベルが多い場合は、1つのupdateハンドラーからすべてを更新し、遠くにいるキャラクターや画面外のキャラクターはスキップします。

ラベルはHTML要素にすることもでき、変換なしで同じCSSピクセルに配置できます。HTMLとCSSを参照してください。

World to Screen

操作​

入力が有効なワールド空間のエレメントは、スクリーン空間のエレメントと同じイベントを、マウス、タッチ、XRコントローラーから受け取ります。カメラは、エレメントがあるレイヤーをレンダリングしている必要があります。スクリーン空間のエレメントが先にテストされ、重なり合うワールド空間のスクリーンの間では、距離に関係なく優先度の高い方がイベントを受け取ります。どのエレメントがイベントを受け取るかを参照してください。

関連情報​