テーマ
UI ヘルパーとレイアウト
ゲームでよく使う UI パーツ(HP バー、メッセージウィンドウ、選択メニューなど)を簡単に作れるヘルパー関数と、ノードの配置を自動計算するレイアウトヘルパーを紹介します。
HP バー
HP や MP のゲージを 3 ノード構成(背景 + 塗り + ラベル)で作成します。残量に応じて色が自動で変わります。
typescript
import { createHpBar } from '@uzuhq/engine-2d';
const hpBar = createHpBar(engine, {
id: 'hp',
x: 20,
y: 400,
width: 200,
height: 18,
label: 'HP',
labelFont: 'bold 11px sans-serif',
colors: { high: '#22c55e', mid: '#eab308', low: '#ef4444' },
});
// 更新(現在値, 最大値)
hpBar.update(75, 100); // 75% → 緑
hpBar.update(40, 100); // 40% → 黄色
hpBar.update(15, 100); // 15% → 赤テキストウィンドウ
タイプライター演出(1 文字ずつ表示)付きのメッセージ表示です。RPG の会話シーンに最適です。
typescript
import { createTextWindow } from '@uzuhq/engine-2d';
const tw = createTextWindow(engine, {
id: 'msg',
x: 20,
y: 300,
width: 560,
height: 80,
font: 'bold 16px sans-serif',
charDelay: 25, // 1 文字あたりのミリ秒
});
// メッセージ表示(タイプライター完了まで await)
await tw.show('勇者のこうげき!');
// 非表示
tw.hide();選択メニュー
キーボード(↑↓ + Enter)とタップの両方に対応した選択メニューです。選択結果のインデックスを返します。
typescript
import { createMenu } from '@uzuhq/engine-2d';
const menu = createMenu(engine, {
id: 'cmd',
x: 20,
y: 440,
width: 270,
font: 'bold 18px sans-serif',
});
// メニューを開く(選択されるまで await)
const index = await menu.open(['たたかう', 'まほう', 'にげる']);
// index: 0, 1, 2
menu.close();仮想ゲームパッド
モバイル端末向けの D-pad + アクションボタンです。engine.raw() でスクリーン空間に描画され、押下中は engine.simulateKeyDown() で合成キー入力を注入します。そのため、既存の isKeyDown() ベースのコードがそのまま動きます。
typescript
import { createVirtualGamepad } from '@uzuhq/engine-2d';
const gamepad = createVirtualGamepad(engine, {
buttons: ['a', 'b'],
keyMap: { a: 'KeyZ', b: 'KeyX' },
autoHide: true, // 非タッチデバイスで自動非表示
});
// 状態の取得
gamepad.isPressed('a'); // boolean
gamepad.direction(); // { x, y } 正規化ベクトル
// 表示制御
gamepad.show();
gamepad.hide();
// 破棄
gamepad.destroy();設定
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
dpadMode | '4way' | '8way' | '8way' | D-pad の方向モード |
buttons | ('a' | 'b')[] | ['a', 'b'] | 表示するボタン |
alpha | number | 0.35 | 透明度 |
autoHide | boolean | false | タッチ非対応デバイスで自動非表示 |
keyMap | object | Arrow + Z/X | 各入力に対応するキーコード |
スクロールコンテナ
ドラッグ操作でスクロールできるコンテナです。内部で clipRect を使って描画領域を制限し、スクロールバーも表示されます。アイテムリストやインベントリ画面に便利です。
typescript
import { createScrollContainer } from '@uzuhq/engine-2d';
const scroll = createScrollContainer(engine, {
id: 'list',
x: 20,
y: 100,
width: 260,
height: 340,
contentHeight: 800, // スクロール可能な全体の高さ
});
// アイテムを追加(自動的にコンテナの子になる)
scroll.addItem('item-0', {
type: 'text',
x: 10,
y: 10,
text: 'Item 1',
fill: '#fff',
});
scroll.addItem('item-1', {
type: 'text',
x: 10,
y: 50,
text: 'Item 2',
fill: '#fff',
});
// プログラムでスクロール
scroll.setScrollY(100);
// コンテンツの高さを変更
scroll.setContentHeight(400);
// 全アイテム削除
scroll.clear();
// コンテナ自体を破棄
scroll.destroy();設定
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
id | string | 必須 | コンテナ ID |
x, y | number | 必須 | 位置 |
width, height | number | 必須 | 表示領域サイズ |
contentHeight | number | 必須 | スクロール可能な全体高さ |
bgColor | string | 'rgba(0,0,20,0.85)' | 背景色 |
borderColor | string | '#4a5568' | 枠線色 |
scrollbarWidth | number | 6 | スクロールバー幅 |
scrollbarColor | string | '#64748b' | スクロールバー色 |
レイアウトヘルパー
ノードの位置を自動計算する純粋関数です。addMany() と組み合わせて使います。
横並び: layoutRow(opts, children)
typescript
import { layoutRow } from '@uzuhq/engine-2d';
const cards = layoutRow({ x: 50, y: 300, gap: 8 }, [
{ id: 'c1', type: 'sprite', image: 'card1', width: 48, height: 64 },
{ id: 'c2', type: 'sprite', image: 'card2', width: 48, height: 64 },
{ id: 'c3', type: 'sprite', image: 'card3', width: 48, height: 64 },
]);
// → c1.x = 50, c2.x = 106, c3.x = 162
engine.addMany(cards);縦並び: layoutColumn(opts, children)
typescript
import { layoutColumn } from '@uzuhq/engine-2d';
const buttons = layoutColumn({ x: 100, y: 50, gap: 10 }, [
{ id: 'b1', type: 'rect', width: 200, height: 40, fill: '#333' },
{ id: 'b2', type: 'rect', width: 200, height: 40, fill: '#333' },
{ id: 'b3', type: 'rect', width: 200, height: 40, fill: '#333' },
]);
// → b1.y = 50, b2.y = 100, b3.y = 150
engine.addMany(buttons);オプション
| プロパティ | 型 | 説明 |
|---|---|---|
x | number | 起点の x 座標 |
y | number | 起点の y 座標 |
gap | number? | ノード間の間隔(デフォルト 0) |
centerIn | number? | 指定した幅(または高さ)の中央に揃える |
アセット管理
画像のロードとキャッシュを管理します。
初期ロード
createEngine() の assets.images で指定した画像は engine.ready で完了を待てます。
typescript
const engine = createEngine({
canvas,
assets: {
images: { player: '/sprites/player.png', enemy: '/sprites/enemy.png' },
},
});
await engine.ready;追加ロード
ゲーム中に追加で画像を読み込めます。
typescript
// 単体
await engine.loadImage('boss', '/sprites/boss.png');
// バッチ(進捗コールバック付き)
await engine.preload(
{
bg: '/sprites/bg.png',
items: '/sprites/items.png',
ui: '/sprites/ui.png',
},
(loaded, total) => {
const pct = Math.floor((loaded / total) * 100);
engine.update('loading', { text: `Loading... ${pct}%` });
},
);キャッシュの取得
typescript
const img = engine.image('player'); // HTMLImageElement | null