In my game, I have these computers that feature a SubViewport which is displayed on the computer mesh’s screen. When you examine the computer in-game, your inputs are routed to the Controls parented under the ScreenViewport and explicitly drive the movement of a TextureRect of a mouse cursor.
I want to make the mouse cursor properly usable, where if you move that cursor over a button control, it will trigger its focus_enter signal, and passing it an InputEvent with a “left mouse press” will activate it. The way I think I want to accomplish this is by fabricating InputEvents in GDScript with the info I want (position of the cursor, translating the player’s interact key to be a “left mouse button press”, etc) and then letting that loose into the SubViewport to work as it would normally.
I’ve tried manually emitting the SubViewport’s gui_input(event) signal, but this didn’t work. Is there a way I can accomplish this?
You can use .push_event(your_input_event) on the SubViewport to replicate or create custom events inside the viewport. I’ve made subviewports into clickable 3D menus with this, duplicating the click event and using .push_event.
Ah, thanks for this insight! I feel like this gets me really close to a working solution. My SubViewport is now getting input events. This is successfully having ‘_input’ get called, but not ‘_gui_input’ or Controls receiving input-related callbacks. Should that work normally, or is there a special setup needed for that?
Huh _gui_input is listed as working in the docs, I haven’t tested it much myself as I try to avoid _gui_input where possible. Tough to recommend any action without code, what types of inputs are you trying to send? Only mouse clicks? are you setting the event’s position too?
I created a blank Godot project that implements SubViewport input pushing with a setup that gives you a “cursor” and a Control-based button. But here’s the important piece of code:
class_name InputTest
extends Node
@export var viewport: SubViewport
@export var cursor: Control
@export var button: Button
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if button:
button.pressed.connect(_on_button_pressed)
func _input(event: InputEvent) -> void:
if event is InputEventKey:
if event.keycode == KEY_ESCAPE: get_tree().quit()
if event is InputEventMouseMotion:
cursor.position += event.relative
cursor.position.x = clampf(cursor.position.x, 0, 512)
cursor.position.y = clampf(cursor.position.y, 0, 512)
event.position = cursor.position
event.global_position = cursor.position
viewport.push_input(event)
if event.is_action_pressed("interact"):
var click: InputEventMouseButton = InputEventMouseButton.new()
click.button_index = 0
click.pressed = true
click.position = cursor.position
click.global_position = cursor.position
viewport.push_input(click)
func _on_button_pressed() -> void:
print("Button Pressed!")
I believe I’m implementing this correctly, but I’m not getting any reactions from the GUI element.
Button index 0 is NONE, this spits out a ton of errors in the debugger, assign it to the proper enum MOUSE_BUTTON_LEFT. Then your scene is setup in such a way that this Control node is blocking mouse input, set “SpriteContainer” mouse filter to Ignore, and the TextureRect too.