Is it possible to @export a method?

Godot Version

4.4.1

Question

Hi everyone!

I would like to create a script component that maps and input event to a method from a node in the scene.

For example, I would like the InputComponent node to trigger a MoveComponent method once an input is pressed:

I would like to emulate the Pick button from Connect Signal window:

Is it possible?

That may sound overengineered, but I wonder if there’s a solution for encapsulating input handling.

Thanks!

If you have a pointer to a node you can invoke any public method in its script by dereferencing:

var food = get_node("../Fruitbowl/Apple")
food.eat()

You can @export a Callable, but it won’t be pickable through the editor. It would be a nice feature, it does raise tough questions and could be difficult to implement while satisfying all use cases.

You could also export a node path and a string to call, which is certainly uglier and doesn’t autocomplete.

@export var caller: Node
@export var function_name: String

###

caller.call(function_name)
1 Like

Yes:

@tool
extends Node


@export var move_left_action = ""
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY) var move_left_method:String
@export_tool_button("Choose method") var choose_move_left = choose_move.bind("move_left")


var editor_interface


func _init() -> void:
	# EditorInterface isn't available on exported projects so
	# we need to load it dynamically and only when the script
	# is running inside the editor.
	if Engine.is_editor_hint():
		editor_interface = Engine.get_singleton("EditorInterface")


func _ready() -> void:
	printt(move_left_action, move_left_method)


func choose_move(option:String) -> void:
	if not Engine.is_editor_hint() or editor_interface == null:
		return

	editor_interface.popup_node_selector(func(node_path:NodePath):
		if node_path.is_empty():
			return
		else:
			var node = editor_interface.get_edited_scene_root().get_node(node_path)
			editor_interface.popup_method_selector(node, func(method:String):
				match option:
					"move_left":
						move_left_method = method
			)
	)

It is :sweat_smile:

It’s really easy using signals to do what you want without exporting a method. At some point signals started showing up in the editor when you create them.

Here’s a screen shot from a game I’m working on now. Both LevelSelect and UIScreen are custom classes.

Based on your screenshots, do something like this:

class_name InputComponent extends Node

signal input_received(direction: Vector2)


func _physics_process(delta: float) -> void:
	var direction := vector2.ZERO
	var direction2 = Input.get_vector("move_left", "move_right","move_up","move_down")
	input_received.emit(direction)
class_name MoveComponenet extends Node

@export var actor: Node2D
@export var speed := 1.0


func _on_input_received(direction: Vector2) -> void:
	actor.position += direction * speed

Then you can hook it up in the editor like any other signal, or add this to you MoveComponent code:

@onready var input_component = "$../InputComponent"

func _ready() -> void:
	input_component.input_received.connect(_on_input_received)

Personally, I add a parent component that handles all the signals like a switchboard. But you may be doing something slightly different than I am, as I have the inputs inside each state, and if the state isn’t in the tree, that things can’t be done.

Thanks everyone for the replies!

@hexgrid I’m actually avoiding calling the method in the script, I was looking for a more generic approach.
@gertkeno don’t worry, that’s not ugly and it is generic just like I need it.
@mrcdk it was exactly what I was looking for, thanks.
@dragonforge-dev good point, will try this approach too.

1 Like