テーマ
入力・ゲームループ・カメラ
ユーザーの入力を受け取り、毎フレームの更新処理を行い、カメラでワールドを覗く。ゲームのインタラクション部分を担う機能をまとめて解説します。
入力イベント
ノードへの直接ハンドラ
最もシンプルな方法です。ノード定義時に onClick / onDown / onUp を指定します。
typescript
engine.add('btn', {
type: 'rect',
x: 50,
y: 400,
width: 200,
height: 50,
fill: '#3b82f6',
onClick: () => {
console.log('ボタンが押された');
},
});グローバルイベント: engine.on(event, handler)
画面全体のタップやキー入力を受け取ります。戻り値の関数で購読を解除できます。
typescript
const unsub = engine.on('tap', (e) => {
console.log(`(${e.x}, ${e.y}) がタップされた。ノード: ${e.id}`);
});
// 不要になったら解除
unsub();イベント一覧
| イベント | ペイロード | 説明 |
|---|---|---|
tap | { id, x, y } | タップ / クリック |
keydown | { key, code, repeat } | キー押下 |
keyup | { key, code } | キー離し |
pointermove | PointerDragEvent | ポインタ移動 |
dragstart | PointerDragEvent | ドラッグ開始 |
drag | PointerDragEvent | ドラッグ中 |
dragend | PointerDragEvent | ドラッグ終了 |
swipe | { direction, distance, speed, startX, startY, endX, endY } | スワイプ |
キーの押下チェック
engine.isKeyDown() でキーが現在押されているか確認できます。ゲームループ内での移動処理に便利です。
typescript
engine.onUpdate((dt) => {
if (engine.isKeyDown('ArrowLeft')) playerX -= speed * dt;
if (engine.isKeyDown('ArrowRight')) playerX += speed * dt;
engine.update('player', { x: playerX });
});合成キー入力
仮想ゲームパッドなどから isKeyDown() を透過的に動かしたい場合に使います。
typescript
engine.simulateKeyDown('ArrowLeft'); // isKeyDown('ArrowLeft') が true になる
engine.simulateKeyUp('ArrowLeft'); // false に戻るゲームループ
engine.onUpdate(callback)
毎フレーム呼ばれるコールバックを登録します。引数 dt は前フレームからの経過時間(秒単位、60fps なら約 0.016)です。
typescript
let ballY = 100;
let velocityY = 0;
const gravity = 500; // px/秒²
const unsub = engine.onUpdate((dt) => {
velocityY += gravity * dt;
ballY += velocityY * dt;
if (ballY > engine.height - 20) {
ballY = engine.height - 20;
velocityY = -velocityY * 0.8;
}
engine.update('ball', { y: ballY });
});
// 不要になったら解除
unsub();dt を使って計算する
フレームレートは端末によって異なります。移動量を計算するときは必ず dt を掛けてください。
typescript
// NG: フレームレートで速度が変わる
playerX += 5;
// OK: 端末に依存しない
playerX += 300 * dt; // 秒速 300pxタイマー
ゲームループに連動したタイマーです。window.setTimeout と違い、シーン遷移時に自動クリアされるので、リソースリークの心配がありません。
typescript
// 一定時間後に実行
const id = engine.setTimeout(() => {
console.log('1秒後に実行');
}, 1000);
// 定期実行
const id2 = engine.setInterval(() => {
spawnEnemy();
}, 2000);
// 手動で解除
engine.clearTimer(id);
engine.clearTimer(id2);衝突判定
矩形ベースの AABB 判定です。
typescript
// 2 つのノード間の衝突判定
if (engine.overlap('player', 'enemy')) {
takeDamage();
}
// ノードとタグ付きノード群のいずれかに衝突しているか
if (engine.overlapAny('player', 'coin')) {
collectCoin();
}
// 衝突しているすべてのノード ID を取得
const hits = engine.overlapAll('bullet', 'enemy');
for (const enemyId of hits) {
engine.remove(enemyId);
}INFO
より高度な衝突判定(円同士、円 vs 矩形、ポリゴンなど)が必要な場合は 物理とタイルマップ を参照してください。
カメラ
カメラを使うと、画面よりも広いワールドの一部を表示できます。RPG のマップ探索やサイドスクロールゲームに必要です。
カメラの設定
typescript
// 直接位置を指定
engine.setCamera({ x: 100, y: 50 });
// プレイヤーに追従
engine.setCamera({
followId: 'player',
followSmooth: 0.1, // 追従の滑らかさ(0〜1、小さいほどゆっくり)
});
// ズーム + 移動範囲の制限
engine.setCamera({
followId: 'player',
followSmooth: 0.12,
zoom: 1.5,
bounds: { minX: 0, minY: 0, maxX: 1200, maxY: 1200 },
});カメラ設定一覧
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
x, y | number | 0 | カメラ位置 |
followId | string | — | 追従するノード ID |
followSmooth | number | 0.1 | 追従の滑らかさ(0〜1) |
zoom | number | 1 | ズーム倍率(>1 でズームイン) |
bounds | {minX, minY, maxX, maxY} | — | カメラ移動の境界 |
カメラの部分更新とアニメーション
typescript
// 設定の一部だけ変更
engine.updateCamera({ zoom: 2.0 });
// ズームイン演出
await engine.animateCamera({ zoom: 1.5 }, { duration: 300, easing: 'easeOutQuad' });
await engine.animateCamera({ zoom: 1.0 }, { duration: 200 });カメラ情報の取得
typescript
engine.getCameraOffset(); // { x, y } 現在のカメラオフセット
engine.getCameraZoom(); // number 現在のズーム倍率
engine.isInView('enemy'); // boolean ノードが画面内にあるかテキスト計測
テキストの描画サイズを事前に知りたい場合に使います。ボタンの幅をテキストに合わせるときなどに便利です。
typescript
const { width, height } = engine.measureText('Hello World', 'bold 16px sans-serif');描画フック
Canvas の ctx に直接描画したい場合のエスケープハッチです。エンジンのノードシステムでは表現しにくいカスタム描画に使います。
engine.raw(fn) — ノード描画の後(HUD 用)
スクリーン空間に描画されます。カメラの影響を受けません。
typescript
const unsub = engine.raw((ctx, w, h) => {
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.fillRect(0, 0, w, 40);
ctx.fillStyle = '#fff';
ctx.fillText('HUD テキスト', 10, 25);
});
unsub(); // 解除engine.rawBelow(fn) — ノード描画の前(背景用)
ワールド空間に描画されます。カメラの影響を受けます。タイルマップの描画に使います。
typescript
engine.rawBelow((ctx, w, h) => {
ctx.fillStyle = '#333';
ctx.fillRect(0, 0, w, h);
});読み取り専用プロパティ
| プロパティ | 説明 |
|---|---|
engine.canvas | Canvas 要素 |
engine.width | 現在の論理幅 |
engine.height | 現在の論理高さ |
engine.designWidth | デザイン幅 |
engine.designHeight | デザイン高さ |