Same here. Tested on Mac and don’t have any problems, both using _unhandled_input in pause_menu as you had it, and if I change it to _input. Could be a Linux issue?
A few side notes about your code, feel free to ignore obviously.
- Consider using the built-in “ui_cancel” instead of making own “menu_changed” action. That way it works on any platform Godot supports. For Mac/PC, that’s ESC key, for consoles it will be w/e the button on w/e controller is default for the console, Android that can be the Back key, etc. So:
if event.is_action_pressed("ui_cancel"):
- Do not expose internal implementation to external objects. For example, calling .focus() on pause_menu from other menus is, imho, a violation of this principle. It shouldn’t be anyone’s concern that pause_menu auto-focuses on display. Instead, define signals in child menu like ‘signal dismissed’ and fire it when user presses back button. In the parent menu, connect to it and name func descriptively, such as:
controls_menu.dismissed.connect(_on_controls_menu_dismissed)
It is the parent menu that should be showing/hiding child menus, not child menu that should be calling .show() and/or .focus() on parent menu.
Why am I saying all this? B/c I decided to test without the focus just to be thorough and then got error after error as .focus() was used in other places in the code and now that method didn’t exist. Changing internal implementation of an object should not break any other objects; this creates maintenance nightmare.
For this reason, tbh, calling show() or hide() on external scenes is just not practical since usually more has to happen for a given scene like new menu to appear. So I usually put that logic in my own display() function on child scene/menu that a parent scene/menu calls.
Maybe you already know and just did it as a shortcut on a test project, ofc, so np, just thought I’d mention it.
- For movement input, a convenient system method is Input.get_vector(). It does 2 things: a) it works whether controls are WASD or arrow keys or controller joystick or dpad (and lets you specify custom dead zone for controllers, if you want), and b) it allows you to reduce your _physics_process by over half, if you’d like:
var move_vector = Input.get_vector("move_left", "move_right", "move_down", "move_up")
move_vector.y *= -1 # adjust to godot coordinate system where "up" is negative
if up_direction == Vector2.UP or up_direction == Vector2.DOWN:
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y += (up_direction * jump).y
elif move_vector.x != 0:
velocity.x = sign(move_vector.x) * speed
elif abs(velocity.x) > 2 and is_on_floor():
velocity.x *= slip
elif is_on_floor():
velocity.x = 0
velocity.y -= up_direction.y * gravity_force
elif up_direction == Vector2.LEFT or up_direction == Vector2.RIGHT:
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.x += (up_direction * jump).x
if move_vector.y != 0:
velocity.y = sign(move_vector.y) * speed
print(velocity.y)
elif abs(velocity.y) > 2 and is_on_floor():
velocity.y *= slip
elif is_on_floor():
velocity.y = 0
velocity.x -= up_direction.x * gravity_force
I feel like the above can be shortened even more, but this is all the time I had.
- I saw you’re manually calculating position for your pause menu, may I ask why? Generally, you want to rely on Godot’s built-in layout methods/controls. For one, do not use any Node2Ds for UI in general, use Control and its children only (in general). But for root nodes specifically, use CanvasLayer as the root node. Just right-click pause_menu root node, select Change Type… and select Canvas Layer. In 20 seconds I changed your tree to this:

Just go to CenterContainer and under Layout → Anchor Preset select “Center”, and for VBoxContainer go to Theme Overrides → Constants and set Separation property (like 30). And boom, menu is auto-positioned in the center for you no matter the resolution, etc.
- This one is inconsequential, but instead of:
if event.is_action_pressed("ui_cancel"):
if visible == false:
return
else:
hide()
print("hidden")
get_tree().paused = false
get_viewport().set_input_as_handled()
You can just write:
if visible and event.is_action_pressed("ui_cancel"):
hide()
print("hidden")
get_tree().paused = false
get_viewport().set_input_as_handled()
Less lines, less indents! 
Hope some of this is helpful, cheers!