0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Godot 4のInputMapで柔軟なキーバインドを実装する

0
Posted at

Godot 4でアクションゲームを作っていると、必ずキーバインドの問題にぶつかります。最初はInput.is_key_pressed(KEY_SPACE)のようなハードコードで済ませますが、そのままでは以下の問題が出てきます。

  • キーボードとゲームパッドに対応したい
  • プレイヤーに設定を変更させたい
  • 設定を保存して次回起動時に読み込みたい
  • 同じアクションに複数のキーを割り当てたい

Godot 4のInputMapシステムを使えば、これらすべてを綺麗に解決できます。この記事では、実際に動くコードで、InputMapの使い方を一通り紹介します。

なぜInputMapを使うのか

一般的な作り方を比較してみましょう。

ハードコード版:

func _physics_process(delta: float) -> void:
    if Input.is_key_pressed(KEY_SPACE):
        jump()
    if Input.is_key_pressed(KEY_Z) or Input.is_joy_button_pressed(0, JOY_BUTTON_X):
        attack()

この書き方だと、新しいキーを追加するたびにコードを修正しないといけません。ゲームパッドの種類ごとにボタン番号が違うので、対応が増えるほど条件式が複雑化します。

InputMap版:

func _physics_process(delta: float) -> void:
    if Input.is_action_pressed("jump"):
        jump()
    if Input.is_action_pressed("attack"):
        attack()

jumpattackはアクション名で、実際にどのキーやボタンが対応するかはプロジェクト設定またはコードから定義できます。

プロジェクト設定でのInputMap定義

Project > Project Settings > Input Map タブから、アクション名を追加してキーバインドを設定します。標準的なプラットフォーマーの場合、move_leftmove_rightjumpattackinteractmenupauseくらいを定義しておくと便利です。

入力の検知方法

1. ボタンが押された瞬間を検知

ジャンプや攻撃のように、押された瞬間に1回だけ反応したい場合:

func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("jump"):
        jump()
    if event.is_action_pressed("attack"):
        attack()

_unhandled_inputを使うことで、UI要素が先に入力を消費した場合はスキップされます。

2. ボタンが押されている間を検知

移動のように、押している間ずっと処理したい場合:

func _physics_process(delta: float) -> void:
    var direction = Input.get_axis("move_left", "move_right")
    velocity.x = direction * SPEED

2D移動ならInput.get_vectorを使うとさらに簡単です:

var input_dir = Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = input_dir * SPEED

3. ボタンが離された瞬間を検知

func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_released("charge"):
        release_charge_attack()

コードからInputMapを変更する

プレイヤーに設定を変更させる場合、コードからアクションの内容を書き換えます。

# Hキーを「heal」アクションに追加
var event = InputEventKey.new()
event.keycode = KEY_H
InputMap.action_add_event("heal", event)

# 既存のキーバインドを置き換え
func rebind_action(action: String, new_event: InputEvent) -> void:
    InputMap.action_erase_events(action)
    InputMap.action_add_event(action, new_event)

キーリバインド画面の実装

プレイヤーに設定を変更させるUIを作る場合:

extends Button

@export var action_name: String
var waiting_for_input: bool = false

func _ready() -> void:
    update_label()
    pressed.connect(_on_pressed)

func update_label() -> void:
    var events = InputMap.action_get_events(action_name)
    text = events[0].as_text() if events.size() > 0 else "未設定"

func _on_pressed() -> void:
    waiting_for_input = true
    text = "キーを押してください..."

func _input(event: InputEvent) -> void:
    if not waiting_for_input:
        return
    if event is InputEventKey and event.pressed:
        InputMap.action_erase_events(action_name)
        InputMap.action_add_event(action_name, event)
        waiting_for_input = false
        update_label()
        get_viewport().set_input_as_handled()

as_text()はキーを人間が読める形式(SpaceCtrl+Aなど)に変換してくれるので便利です。

設定の保存と読み込み

InputMapの変更はメモリ上だけなので、ConfigFileで保存します。

const CONFIG_PATH = "user://input_config.cfg"

func save_input_map() -> void:
    var config = ConfigFile.new()
    for action in InputMap.get_actions():
        if action.begins_with("ui_"):
            continue
        var events = InputMap.action_get_events(action)
        var event_data: Array = []
        for event in events:
            if event is InputEventKey:
                event_data.append({"type": "key", "keycode": event.keycode})
            elif event is InputEventJoypadButton:
                event_data.append({"type": "joy", "button": event.button_index})
        config.set_value("input", action, event_data)
    config.save(CONFIG_PATH)

ui_で始まるアクションはGodotが自動で管理するものなので、保存対象から除外しています。

ゲームパッド対応

プロジェクト設定のInput Mapで、jumpアクションにInputEventKey (Space)InputEventJoypadButton (Button 0 = A)を両方追加すると、どちらからでも発火します。

デッドゾーンの設定も重要です。アナログスティックで移動する場合、Input Map設定で Deadzone を 0.2 程度に設定しておくと誤入力が減ります。

よくある落とし穴

  • _inputはすべての入力を拾いますが、_unhandled_inputはUIが処理しなかった入力だけを拾います
  • ui_プレフィックスのアクションを上書きすると、デフォルトUI操作が動かなくなります
  • カスタムアクションはplayer_などのプレフィックスを付けると安全です

AIエージェントでInputMapコードを生成する

InputMapのコードはボイラープレートが多いのが悩みどころです。リバインド画面を作るときに毎回似たコードを書くことになります。

私はZivaというGodot専用のAIエージェントを使っています。「キーボードとゲームパッド両対応のリバインド画面を作って、設定を自動保存するようにして」と指示すると、ボタンとSignal、設定ファイルの読み書きまで含めた一式を生成してくれます。私自身がZivaを作っている側なのでバイアスはありますが、Godot 4固有のAPI(InputMapの正しいメソッド名など)をちゃんと使ってくれるのは便利です。通常のChatGPTはたまにGodot 3のAPIを混ぜて答えてきます。

まとめ

InputMapはGodot 4の入力系の中核です。最初のうちはInput.is_action_pressedを使うだけでも十分ですが、リバインド画面や設定保存を作ろうとするとInputMap.action_add_eventなどのAPIも必要になります。

公式のInputMap ドキュメントには全メソッドのリファレンスがあります。入力システムは後から変更しようとすると地獄なので、プロジェクトの最初の段階でInputMapを使う設計にしておくことを強くおすすめします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?