【Godot】キーコンフィグを作る
はじめに
PC向けにゲームを作ると様々な入力デバイスがあることに気づきます。
Godotではプロジェクト設定からインプットマップを設定できますね。
複数のデバイスを1つのactionに割り当てることができます。
しかし、キーアサインを好みに設定したいプレイヤーもいて、キーコンフィグ機能があるゲームも多いです。
本記事ではインプットマップを実行時に設定する方法を解説します。
InputMapを使う
プロジェクト設定から設定したインプットマップは、InputMap
というAPIでアクセスできます。今回はこのAPIを主に使います。
キーコンフィグを作る
今回は下記のようにキーボードとゲームパッド両方から操作できるような設定です。
InputMapの設定
ここでは入力イベントを受け取り、InputMapに上書きする方法を解説します。
InputMap設定メソッド
InputMapに設定します。設定を上書きするAPIは存在しないので、一旦InputMap.action_erase_events
で削除してからInputMap.action_add_event
でInputEventを設定します。
func set_inputmap(action: String, event_index: int, event: InputEvent): # InputMapからactionを取得 var list = InputMap.get_action_list(action) # actionを一旦削除 InputMap.action_erase_events(action) # リストを上書き list[event_index] = event # actionを改めて追加 InputMap.action_add_event(action, list[0]) # キー設定 InputMap.action_add_event(action, list[1]) # パッド設定
入力を受けるメソッド
_gui_input
で入力を受け付けます (Controlノードにスクリプトを付けます)
受け取ったInputEventをset_inputmap
に設定します。
func _gui_input(event: InputEvent): var action = "act_jump" # 暫定的にact_jump固定 if event is InputEventKey: # キー入力された var key := event as InputEventKey if key.is_pressed(): set_inputmap(action, 0, event) elif event is InputEventJoypadButton: # パッド(ボタン)入力された var joypad_button := event as InputEventJoypadButton if joypad_button.is_pressed(): set_inputmap(action, 1, event) elif event is InputEventJoypadMotion: # パッド(スティック)入力された var joypad_motion := event as InputEventJoypadMotion if joypad_motion.axis_value != 0: set_inputmap(action, 1, event)
InputMapのセーブとロード
InputMapは再起動するとプロジェクト設定のデータにリセットされます。
キーコンフィグ機能として成り立たせるためには、設定時にファイルにコンフィグ情報を保存して、ゲーム起動時にロードする必要があります。
保存対象のaction
const input_actions := [ "act_attack", "act_jump", "act_up", "act_down", "act_left", "act_right", "ui_accept", "ui_cancel", "ui_up", "ui_down", "ui_left", "ui_right", ]
InputMapのシリアライズ
設定したInputMapを保存するときに使います。
func _serialize_input() -> Dictionary: var data = {} for action in input_actions: var list = InputMap.get_action_list(action) var item = {} item["key"] = list[0].scancode if list[1] is InputEventJoypadButton: item["button"] = list[1].button_index elif list[1] is InputEventJoypadMotion: item["axis"] = list[1].axis item["axis_dir"] = 1 if list[1].axis_value > 0 else -1 data[action] = item return data
InputMapのデシリアライズ
保存されたコンフィグ情報をInputMapに再設定するときに使います。
func _deserialize_input(data): if not data is Dictionary: return for action_name in data: var item = data[action_name] as Dictionary if not item: continue var list = InputMap.get_action_list(action_name) as Array if not list: continue if item.has("key"): var keycode = item["key"] if typeof(keycode) == TYPE_REAL: list[0].scancode = keycode if item.has("button"): var button_index = item["button"] if typeof(button_index) == TYPE_REAL: var event = InputEventJoypadButton.new() event.button_index = button_index list[1] = event elif item.has("axis") and item.has("axis_dir"): var axis_id = item["axis"] var axis_dir = item["axis_dir"] if typeof(axis_id) == TYPE_REAL and typeof(axis_dir) == TYPE_REAL: var event = InputEventJoypadMotion.new() event.axis = axis_id event.axis_value = 1 if axis_dir > 0 else -1 list[1] = event InputMap.action_erase_events(action_name) InputMap.action_add_event(action_name, list[0]) InputMap.action_add_event(action_name, list[1])
JSONにセーブ
シリアライズしたInputMapの情報をJSON化してファイルに保存します。
func savefile(): var file := File.new() if file.open("user://settings.json", File.WRITE) != OK: return var jsonstr := JSON.print({ "input": _serialize_input(), }, " ") file.store_string(jsonstr) file.close()
JSONからロード
JSON形式で読み込んだ情報をInputMapに再設定します。
func loadfile(): var file := File.new() if file.open("user://settings.json", File.READ) != OK: return var jsonstr := file.get_as_text() file.close() if not jsonstr: return var json_res := JSON.parse(jsonstr) if json_res.error: printerr("failed to load settings: " + json_res.error_string) var json := json_res.result as Dictionary _deserialize_input(json.get("input"))
注意
JSONはユーザーでもテキストエディタを使っていじれるため、 エラー処理をしっかりしないとセキュリティホールになるかもしれないので注意です。