チャプター 02 / 06

2. 入力を受ける

キーボード・マウス・ゲームパッドの入力を MitiruEngine でどう拾うか。C++ 側の InputMapper / BridgeActionRouter と、CEF 側 (runtime/mitiru_input.js) で UI クリックを C++ に送る方法を一通り扱う。

入力はゲームの種類で形が変わります。アクションは毎フレームのキー押下、RPG はキー単発と UI クリック、RTS はドラッグとモディファイア、シミュレーションはパネル操作 ── どれも MitiruEngine では「C++ 側で意味を決める、CEF 側は発火するだけ」が基本です。

具体的には:

  • キーボード / マウス / ゲームパッドの raw 入力 は C++ 側で受けて InputMapper で「Action (例: Jump, Fire, ConfirmMenu)」に変換する
  • HTML 上のボタンや UI クリック は CEF 側で window.cefQuery(...) を呼んで C++ に「何が起きたか」を通知する
  • C++ 側は BridgeActionRouter で受けて、シーンの状態を更新する

JS 側にゲームロジックを書かないので、入力の解釈はどんなジャンルでも 1 か所 (C++) にまとまります。

入力フロー device → InputMapper → Action enum → ゲームロジック という流れ。CEF からの UI イベントは cefQuery 経由で同じパイプに合流する。

Device key / mouse / pad

InputMapper (C++) bindKey / isDown / wasPressed

Action enum Jump / Fire / Pause

Game logic update(dt)

HTML button UI クリック

window.cefQuery ui|pause-pressed

BridgeActionRouter registerHandler

図: device 入力と UI クリックは別経路で来るが、最終的には C++ 側の Game logic に合流する。

C++ で Action に束ねる

#include <mitiru/input/InputMapper.hpp>
#include <mitiru/input/BridgeActionRouter.hpp>

enum class Action { MoveLeft, MoveRight, Fire, Pause };

mitiru::input::InputMapper<Action> mapper;
mapper.bindKey(Action::MoveLeft,  mitiru::input::Key::A);
mapper.bindKey(Action::MoveRight, mitiru::input::Key::D);
mapper.bindKey(Action::Fire,      mitiru::input::Key::Space);
mapper.bindKey(Action::Pause,     mitiru::input::Key::Escape);

void update(float dt) {
    if (mapper.isDown(Action::MoveLeft))  player.x -= speed * dt;
    if (mapper.isDown(Action::MoveRight)) player.x += speed * dt;
    if (mapper.wasPressed(Action::Fire))  spawnBullet();
    if (mapper.wasPressed(Action::Pause)) pushScene<PauseScene>();
}

シューティングなら isDown で連射、RTS の単発コマンドなら wasPressed で一度だけ反応、と使い分けます。

CEF 側から C++ にイベントを送る

HUD のボタンや確認ダイアログのクリックは HTML 上に置きます。発火だけ C++ に送って、状態の更新は C++ 側で処理します。

<button id="btn-pause">Pause</button>
<script>
  document.getElementById('btn-pause').addEventListener('click', () => {
    // signal-only bridge: 「何が起きたか」だけ通知する
    window.cefQuery({ request: 'ui|pause-pressed' });
  });
</script>

C++ 側はハンドラを登録するだけ。

engine.cefContext().registerHandler("ui", [&](const std::string& payload) {
    if (payload == "pause-pressed") game.requestPause();
    return std::string{"ok"};
});

ランタイムランドラを使う

web/mitiru_runtime/mitiru_input.js は、キー入力 / マウス位置 / ボタン状態を C++ 側に簡単に流すための薄いラッパです。アクション系のように HTML 上で UI を出しつつ C++ がメインの入力を持つ構成で便利です。

関連 API

もっと深く知る