setup-input-map
Skill2 starsChanged 6 months agoMITView on GitHub
Configure Godot 4.x InputMap with standard actions (move, jump, attack, interact, pause, UI nav) for keyboard, mouse, and gamepad. Generate a runtime remap UI as a bonus. Edits project.godot.
In this repository33 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- bootstrap-godot-projectSkill
- codebase-surveySkill
- create-autoloadSkill
- create-componentSkill
- create-resourceSkill
- create-sceneSkill
- create-state-machineSkill
- export-configSkill
- feature-planSkill
- feature-specSkill
- game-brainstormingSkill
- gdd-writerSkill
- genre-pack-3d-actionSkill
- genre-pack-platformerSkill
- genre-pack-topdownSkill
- genre-pack-turnbasedSkill
- godot-patternsSkill
- gut-test-writerSkill
- networking-foundationSkill
- performance-auditSkill
- save-schema-migrationSkill
- setup-collision-layersSkill
- setup-git-godotSkill
- setup-localizationSkill
- setup-save-systemSkill
- sfx-generatorSkill
- shader-writerSkill
- subagent-dev-modeSkill
- ui-patterns-godotSkill
- update-docsSkill
- using-godot-superpowersSkill
- writing-game-planSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 215
Use physical_keycode (positional) not keycode (layout-dependent) for movement keys — works across QWERTY / AZERTY / Dvorak.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Setup Input Map
Configures Godot 4.x InputMap actions and provides a remap UI scaffold.
Default actions (genre-agnostic)
| Action | Keyboard | Gamepad | Notes |
move_left | A, Left | LStick left, DPad left | |
move_right | D, Right | LStick right, DPad right | |
move_up | W, Up | LStick up, DPad up | |
move_down | S, Down | LStick down, DPad down | |
jump | Space | A (Xbox) / X (PS) | platformer / 3d |
attack | LMB, J | RT / R2 | |
attack_secondary | RMB, K | LT / L2 | block / heavy |
interact | E | Y / Triangle | |
dash | Shift | B / Circle | |
inventory | I | Select / Share | |
pause | Esc | Start / Options | |
ui_accept | Enter, Space | A | also auto-mapped by engine |
ui_cancel | Esc | B | engine default |
ui_left/right/up/down | arrows | DPad | engine default |
ui_* actions exist by default — adjust as needed but don't remove (Control nodes rely on them).
project.godot — [input] section
[input]
move_left={
"deadzone": 0.5,
"events": [
Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null),
Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null),
Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null),
Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":false,"script":null)
]
}(That's just move_left. The full block is verbose — easier to add actions through the editor's Input Map panel and let Godot serialize. Recommended: open Project Settings → Input Map, click "Add" for each action listed above, bind keys/buttons, then save.)
Physical keycode reference (physical_keycode):
- A=65, S=83, D=68, W=87
- Space=32
- Left=4194319, Right=4194321, Up=4194320, Down=4194322
- Esc=4194305, Enter=4194309
- Shift=4194325
Gamepad button indices (Xbox layout):
- 0=A, 1=B, 2=X, 3=Y
- 4=LB, 5=RB
- 6=Select, 7=Start
- 11=DPad up, 12=DPad down, 13=DPad left, 14=DPad right
Joypad axes:
- 0=LStick X, 1=LStick Y, 2=RStick X, 3=RStick Y
- 4=LT, 5=RT
Reading input in code
# Movement vector (handles 8-direction + analog stick)
var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity.x = input_dir.x * MAX_SPEED
# One-shot in _unhandled_input (preferred for actions)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("jump"):
_try_jump()
elif event.is_action_pressed("pause"):
_toggle_pause()
# Held check in _physics_process
if Input.is_action_pressed("attack"):
_charge_attack(delta)
# Just-pressed in _physics_process (alternative to _unhandled_input)
if Input.is_action_just_pressed("dash"):
_dash()Genre-specific additions
Platformer
jump(already in defaults)crouch— Ctrl, Down (modifier)wall_grab— Shift held
3D action / FPS
look_left/right/up/down(mouse fallback for keyboard-only)aim— RMB / L2fire— LMB / R2reload— R / X(Square)zoom_in/out— scroll wheel
Top-down shooter
- Same as 3D action, omit jump
Turn-based
confirm— Space, Enter, Acancel— Esc, Bnext_unit— Tab, RBend_turn— F, Start
Runtime remap UI
scenes/ui/input_remap_menu.tscn skeleton. Mark ActionList and ResetButton as unique names (%ActionList, %ResetButton) in the editor so the controller can grab them:
InputRemapMenu (Control)
├── VBoxContainer
│ ├── ScrollContainer
│ │ └── ActionList (VBoxContainer, unique name %ActionList — action rows generated at runtime)
│ └── HBoxContainer
│ ├── ResetButton (Button, unique name %ResetButton, text "Reset to defaults")
│ └── BackButton (Button, optional)Controller scripts/ui/input_remap_menu.gd:
extends Control
const REMAPPABLE := [
"move_left", "move_right", "move_up", "move_down",
"jump", "attack", "interact", "dash", "pause",
]
const SAVE_PATH := "user://input_map.cfg"
@onready var _list: VBoxContainer = %ActionList
@onready var _reset_button: Button = %ResetButton
var _capturing_action: StringName = &""
var _row_for_action: Dictionary[StringName, HBoxContainer] = {}
func _ready() -> void:
_reset_button.pressed.connect(_on_reset_pressed)
_load_overrides()
_build_rows()
func _on_reset_pressed() -> void:
InputMap.load_from_project_settings()
DirAccess.remove_absolute(SAVE_PATH)
_build_rows()
func _build_rows() -> void:
for child in _list.get_children():
child.queue_free()
for action in REMAPPABLE:
var row := HBoxContainer.new()
var label := Label.new()
label.text = action
label.custom_minimum_size.x = 200
var btn := Button.new()
btn.text = _format_event(InputMap.action_get_events(action))
btn.pressed.connect(_capture.bind(action, btn))
row.add_child(label)
row.add_child(btn)
_list.add_child(row)
_row_for_action[action] = row
func _capture(action: StringName, btn: Button) -> void:
_capturing_action = action
btn.text = "press a key..."
func _input(event: InputEvent) -> void:
if _capturing_action == &"":
return
if event is InputEventKey or event is InputEventMouseButton or event is InputEventJoypadButton:
if event.pressed:
InputMap.action_erase_events(_capturing_action)
InputMap.action_add_event(_capturing_action, event)
_save_overrides()
_build_rows()
_capturing_action = &""
get_viewport().set_input_as_handled()
func _format_event(events: Array[InputEvent]) -> String:
if events.is_empty():
return "(unbound)"
return events[0].as_text()
func _save_overrides() -> void:
var cfg := ConfigFile.new()
for action in REMAPPABLE:
var events := InputMap.action_get_events(action)
cfg.set_value("input", action, events)
cfg.save(SAVE_PATH)
func _load_overrides() -> void:
var cfg := ConfigFile.new()
if cfg.load(SAVE_PATH) != OK:
return
for action in REMAPPABLE:
var events: Array = cfg.get_value("input", action, [])
if events.is_empty():
continue
InputMap.action_erase_events(action)
for ev in events:
InputMap.action_add_event(action, ev)Persists to user://input_map.cfg and restores at startup.
Notes
- Use
physical_keycode(positional) notkeycode(layout-dependent) for movement keys — works across QWERTY / AZERTY / Dvorak. Input.is_action_pressed()is fine in_physics_process; for one-shots prefer_unhandled_inputso UI captures take precedence.- For couch co-op, set
deviceon eachInputEventand read withInput.is_joy_known(device).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

