How to add an InputEvent to InputMap with code?

func _ready() -> void:
	add_action_with_keycode("move_front", KEY_W)
	add_action_with_keycode("move_back", KEY_S)
	add_action_with_keycode("move_left", KEY_A)
	add_action_with_keycode("move_right", KEY_D)
	add_action_with_keycode("jump", KEY_SPACE)

func add_action_with_keycode(action, key):
	var event = InputEventKey.new()
	event.physical_keycode = key
	InputMap.add_action(action)
	InputMap.action_add_event(action, event)
CharacterBody3D Example
extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 4.5

func _ready() -> void:
	add_action_with_keycode("move_front", KEY_W)
	add_action_with_keycode("move_back", KEY_S)
	add_action_with_keycode("move_left", KEY_A)
	add_action_with_keycode("move_right", KEY_D)
	add_action_with_keycode("jump", KEY_SPACE)

func add_action_with_keycode(action, key):
	var event = InputEventKey.new()
	event.physical_keycode = key
	InputMap.add_action(action)
	InputMap.action_add_event(action, event)

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("move_left", "move_right", "move_front", "move_back")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()


References:

Personal experiment with updating godot.project with new input keys.

After launching the game it should add new inputs to the godot.project file.
However they are unrecognized by gdscript for some reason and probably need enforcement using previously discussed InputMap. Update: apparently it is because I added @tool above extends CharacterBody3D and reloaded the current project. To resolve: simply don’t use @tool in a CharacterBody3D.

extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 4.5

func _ready():

	setup_input_actions()

func setup_input_actions():
	# Define your input actions here
	add_action("jump", KEY_SPACE)
	add_action("move_left", KEY_A)
	add_action("move_right", KEY_D)
	add_action("move_forward", KEY_W)
	add_action("move_backward", KEY_S)
	# Save to project.godot
	ProjectSettings.save()
	print("Input actions saved to Project Settings.")

func add_action(action_name: String, key_scancode: int):
	var event := InputEventKey.new()
	event.physical_keycode = key_scancode
	
	var property := "input/%s" % action_name
	var action: Dictionary = ProjectSettings.get_setting(property, { "deadzone": 0.5, "events": [] })
	
	# Check for existing events to avoid duplicates
	var event_exists := false
	for existing_event in action["events"]:
		if is_same_key_event(existing_event, event):
			event_exists = true
			break
	
	if not event_exists:
		action["events"].append(event)
		ProjectSettings.set_setting(property, action)
		
		# Update InputMap for immediate use in the editor
		if not InputMap.has_action(action_name):
			InputMap.add_action(action_name)
		InputMap.action_add_event(action_name, event)

func is_same_key_event(a: InputEvent, b: InputEvent) -> bool:
	if a is InputEventKey and b is InputEventKey:
		return a.physical_keycode == b.physical_keycode
	return false

func add_action_with_keycode(action, key):
	var event = InputEventKey.new()
	event.physical_keycode = key
	InputMap.add_action(action)
	InputMap.action_add_event(action, event)

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()