Skip to content

入力・ゲームループ・カメラ

ユーザーの入力を受け取り、毎フレームの更新処理を行い、カメラでワールドを覗く。ゲームのインタラクション部分を担う機能をまとめて解説します。

入力イベント

ノードへの直接ハンドラ

最もシンプルな方法です。ノード定義時に 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 }キー離し
pointermovePointerDragEventポインタ移動
dragstartPointerDragEventドラッグ開始
dragPointerDragEventドラッグ中
dragendPointerDragEventドラッグ終了
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, ynumber0カメラ位置
followIdstring追従するノード ID
followSmoothnumber0.1追従の滑らかさ(0〜1)
zoomnumber1ズーム倍率(>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.canvasCanvas 要素
engine.width現在の論理幅
engine.height現在の論理高さ
engine.designWidthデザイン幅
engine.designHeightデザイン高さ