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

よく使うウィジェット

UIコンポーネントは、UIを組み立てるための部品です。このページでは、それらを組み合わせて、ほとんどのインターフェースに必要なウィジェットを作ります。各レシピでは、エディターまたはコードで構築するヒエラルキーと、それを動作させるコードを示します。

プログレスバーとヘルスバー​

health bar イメージエレメント:背景、300 × 30
└── track グループエレメント:Stretchアンカー、マージン4、フィルが占められる範囲
└── fill イメージエレメント:Stretchアンカー、マージン0、色はバーの色

フィルの右アンカーは0から1までの値に追従するので、フィルはトラックのその割合を占め、トラックによってバーの内側に縁が残ります。

function setHealth(value) {
fill.element.anchor = new pc.Vec4(0, 0, value, 1);
}
setHealth(0.7);

この方法では、テクスチャを使ったフィルは押しつぶされます。代わりにテクスチャを切り取るには、フィルをトラックの左端にアンカーし、ピボットも左端に置いて、widthとrectを一緒に設定します(トラックの幅を使ったfill.element.width = value * 292と、fill.element.rect = new pc.Vec4(0, 0, value, 1))。詳しくは、プログレスバーとローディングサークルのチュートリアルを参照してください。

スライダー​

ハンドルがトラックより小さいスクロールバーは、スライダーになります。ハンドルをドラッグするとvalueが0から1まで変化し、垂直のスクロールバーでは上端が0です。

volume イメージエレメント:トラック、300 × 20、水平方向のScrollbarコンポーネント付き
└── handle 入力を有効にしたイメージエレメント:トラックの高さに合わせてアンカー
volume.scrollbar.handleSize = 0.1;
volume.scrollbar.value = 0.8;
volume.scrollbar.on('set:value', (value) => {
app.systems.sound.volume = value;
});

トグルとラジオグループ​

トグルは、見た目で状態を示すボタンです。

music toggle 入力を有効にしたイメージエレメント、Buttonコンポーネント付き
└── check イメージエレメント:チェックマーク、トグルがオンのときに表示
let musicOn = true;
musicToggle.button.on('click', () => {
musicOn = !musicOn;
musicToggle.findByName('check').enabled = musicOn;
});

ラジオグループは、こうしたボタンをまとめたグループエレメントで、一度にオンになるのはそのうち1つだけです。

const options = difficulty.children;
for (const option of options) {
option.button.on('click', () => {
for (const other of options) {
other.findByName('check').enabled = other === option;
}
});
}

モーダルダイアログは、インターフェースの他の部分を覆い、ダイアログが閉じられるまでその入力をブロックします。

screen
├── hud …
└── dialog グループエレメント:Stretchアンカー
├── backdrop イメージエレメント:Stretchアンカー、黒、不透明度0.6、入力を有効化
└── panel 入力を有効にしたイメージエレメント:テキストとボタンを含むダイアログ本体

ダイアログはヒエラルキーの最後にあるため、スクリーンの他の部分より上に描画され、最初に入力を受け取ります。背景幕はスクリーン全体を覆い、入力が有効になっているため、パネルから外れたクリックをすべて受け止め、その下にあるHUDには何も届きません。パネルの空いている部分へのクリックが背景幕に届かないように、パネルでも入力を有効にしています。ダイアログへのクリックがゲームにも届かないようにするには、クリックの伝播を止めます。UIの入力がゲームに届かないようにするを参照してください。

function openDialog() {
dialog.enabled = true;
}
function closeDialog() {
dialog.enabled = false;
}

// パネルの外側をクリックするとダイアログを閉じる
dialog.findByName('backdrop').element.on('click', closeDialog);

複数のスクリーンの上にダイアログを表示するには、より高い優先度を持つ専用のスクリーンにダイアログを配置します。

ツールチップ​

ツールチップは、ポインターがエレメントに重なっている間に表示されるパネルです。

// ツールチップはアイコンの子で、アイコンの上にアンカーされている
icon.element.useInput = true;
icon.element.on('mouseenter', () => {
tooltip.enabled = true;
});
icon.element.on('mouseleave', () => {
tooltip.enabled = false;
});

子は、親の後に続く兄弟よりも先に描画されるため、それらに覆われることがあります。ツールチップをすべての上に描画するには、代わりにスクリーンの最後の子にして、表示するときにエレメントの位置へ移動します。タッチスクリーンにはホバーがないため、長押しでツールチップを表示するか、その情報を画面に表示しておきます。

動的リスト​

リーダーボードやインベントリのように、項目がデータから作られるリストは、データから子を作成するレイアウトグループです。リストが領域を超えて大きくなる可能性がある場合は、スクロールビューの中に入れます。

function showScores(scores) {
// 古い行を削除してから、スコアごとに行を1つ追加する
for (const row of [...list.children]) {
row.destroy();
}
for (const { name, score } of scores) {
const row = new pc.Entity(name);
row.addComponent('element', {
type: pc.ELEMENTTYPE_TEXT,
fontAsset: font.id,
text: `${name} ${score}`,
fontSize: 28
});
list.addChild(row);
}
}

showScores([{ name: 'Ada', score: 1250 }, { name: 'Grace', score: 990 }]);

ドラッグ&ドロップ​

ElementDragHelperは、ドラッグされている間、エレメントをポインターに追従させます。エレメントの入力が有効になっている必要があり、アプリケーションにはマウスまたはタッチのデバイスが必要です。

const drag = new pc.ElementDragHelper(card.element);
const home = card.getLocalPosition().clone();

// エレメントの境界。キャンバスの左上からのCSSピクセル
const bounds = (entity) => {
const [bottomLeft, , topRight] = entity.element.canvasCorners;
return { left: bottomLeft.x, right: topRight.x, top: topRight.y, bottom: bottomLeft.y };
};

drag.on('drag:end', () => {
const a = bounds(card);
const b = bounds(slot);
const overSlot = a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top;
// カードをスロットにはめ込むか、元の位置に戻す
if (overSlot) {
card.setPosition(slot.getPosition());
} else {
card.setLocalPosition(home);
}
});

1つの軸に沿ってだけドラッグさせるには、2番目の引数に'x'または'y'を渡します。drag:startとdrag:moveは、ドラッグの開始時と継続中に発火します。

テキストフィールド​

UIシステムにはテキスト入力のエレメントがありません。テキストを入力させるには、キャンバスの上にHTMLの<input>を配置します。これにより、プレイヤーはブラウザのキーボード、テキスト選択、オートコンプリート、アクセシビリティも利用できます。HTMLとCSSを参照してください。キャンバス内インターフェースの他の部分と見た目をそろえたフィールドについては、UIテキスト入力チュートリアルを参照してください。

UIのアニメーション​

エレメントのアニメーションは、プロパティを変えることで行います。対象は、エンティティの位置、回転、スケールと、エレメントの色、不透明度、アンカー、マージンです。これらを毎フレーム少しずつ変更するか、トゥイーンライブラリを使って変更します。

// パネルを0.5秒かけて左からスライドインさせる
let t = 0;
const handle = app.on('update', (dt) => {
t = Math.min(t + dt / 0.5, 1);
const eased = 1 - Math.pow(1 - t, 3);
panel.setLocalPosition(pc.math.lerp(-600, 0, eased), 0, 0);
if (t === 1) handle.off();
});

不透明度は子に継承されないため、パネルをフェードさせるには、その下にあるすべてのエレメントをフェードさせます。

function setOpacity(entity, opacity) {
if (entity.element && entity.element.type !== pc.ELEMENTTYPE_GROUP) {
entity.element.opacity = opacity;
}
for (const child of entity.children) {
setOpacity(child, opacity);
}
}

この関数はすべてのエレメントに同じ不透明度を設定するため、エレメントごとに不透明度が異なる場合は、先に元の不透明度を保存しておきます。ボタンはfadeDurationでティント間をフェードします。トゥイーンライブラリについては、トゥイーンチュートリアルを参照してください。

ローディング画面とシーンの切り替え​

  • ローディング画面。 エディターのプロジェクトは、シーンが読み込まれる前、つまりUIが読み込まれる前にローディング画面を表示するため、ローディング画面はHTMLとCSSで作ります。ローディング画面を参照してください。
  • シーンの切り替え。 シーンに属するインターフェースは、シーンと一緒に破棄されます。シーンを切り替えてもHUDを残すには、すべてを置き換えるのではなく、新しいシーンのヒエラルキーをHUDと並べて読み込みます。シーンの読み込みとシーンの切り替えチュートリアルを参照してください。

タッチジョイスティック​

タッチデバイス向けの画面上のジョイスティックやボタンは、タッチイベントを読み取るエレメントです。タッチスクリーンのジョイパッド操作チュートリアルでは、再利用できるツインスティックのレイアウトを作成します。

関連情報​