Phase 2
System Architecture Series
これまでの流れ:
- #08 ループ設計 → 毎フレームの構造を作った
- #09 Fixed Time Step → 時間を安定させた
- #10 状態管理 → update と render を分離した
- #11 Interpolation → 見た目を滑らかにした
- #12 Canvas → 描画を整理した
そして今回。
「ユーザーの操作を、どうやってゲーム側に伝えるのか?」
ここを設計します。
❌ よくある初心者パターン
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowRight") {
player.x += 5;
}
});一見問題なさそうですが、これには大きな問題があります。
問題1:イベント発火回数に依存する
キーボードのリピート速度は環境依存。
問題2:Fixed Time Stepと相性が悪い
update() の中で時間制御しているのに、
イベント側で直接状態を変更してしまう。
問題3:設計がバラける
入力処理があちこちに散らばる。
✅ 正しい考え方
入力は「イベント」ではなく、
“入力状態(Input State)” を作る
1.入力は「記録する」だけ
①入力管理クラスを作る
class Input {
constructor() {
this.keys = {};
window.addEventListener("keydown", (e) => {
this.keys[e.key] = true;
});
window.addEventListener("keyup", (e) => {
this.keys[e.key] = false;
});
}
isDown(key) {
return !!this.keys[key];
}
}②update内で参照する
class Player {
constructor(input) {
this.x = 100;
this.speed = 200;
this.input = input;
}
update(dt) {
if (this.input.isDown("ArrowRight")) {
this.x += this.speed * dt;
}
if (this.input.isDown("ArrowLeft")) {
this.x -= this.speed * dt;
}
}
}🔥 何が良いのか?
- 入力とロジックが分離される
- Fixed Time Stepと完全に整合する
- フレームレートに依存しない
- Scene切替にも強い
2.「イベント駆動」ではなく「ポーリング」
❌ イベント駆動
押された瞬間に処理する
✅ ポーリング
毎フレーム「今どうなっているか?」を確認する
ゲームループ設計ではポーリングが基本です。
マウスも同じ考え方
class Input {
constructor(canvas) {
this.mouse = { x: 0, y: 0, down: false };
canvas.addEventListener("mousemove", (e) => {
this.mouse.x = e.offsetX;
this.mouse.y = e.offsetY;
});
canvas.addEventListener("mousedown", () => {
this.mouse.down = true;
});
canvas.addEventListener("mouseup", () => {
this.mouse.down = false;
});
}
}3.設計レベルが1段上がる瞬間
Inputは「橋」です。
User → Input → Game State → Renderこれがシリーズ後半の核になります。
4.実験:「イベント直接制御 vs 入力状態管理」比較デモ
実験内容
- 左:イベントで直接 x += 5
- 右:Inputクラス + dt制御
👀 観察ポイント
- キーを押しっぱなしにする
- PC負荷を上げる
- フレームレートを落とす
👉 右側は常に一定速度で動く
👉 左側は環境依存で不安定
5.まとめ
Input設計とは
「イベントを状態に変換すること」
これが出来ると:

コメントを残す