.connect(event.emit.bind(value)) works in editor but breaks in build

Godot Version

4.3

Question

Does anyone know what’s happening here?

Works in Editor

:white_check_mark: This code works when I used the editor (game starts fine, no errors reported)…

button.pressed.connect(EventManager.weapon_unlocked.emit.bind(unlocked_scene))

(“EventManager” is my Signal-Hub, “unlock_scene” is class PackedScene)

Breaks in Build

:stop_sign:…BUT when I export a build, I get these errors (see below). The game runs (no crashes) but some scripts are broken:

An even simpler call where I bind a simple integer does not work neither:

# Does not matter if Signal is defined in EventManager or in the 
# same script as the .connect happens. Always breaks in build.
signal test(hello : int)

# Works in Editor, breaks in Build
new_button.pressed.connect(EventManager.test.emit.bind(1))

Workaround #1 (works)

This is my current workaround. Instead if emitting the signal directly in .connect, I call a function which then calls the emit:

button.pressed.connect(weapon_unlocked.bind(unlock_scene))

...

func weapon_unlocked(unlocked_scene : PackedScene) -> void:
	EventManager.weapon_unlocked.emit(unlocked_scene)

Workaround #2 (works)

Thanks @Locher ! This works as well:

new_button.pressed.connect(func() -> void: EventManager.weapon_unlocked.emit(unlock_scene))

Code

Here is the complete code (using workaround #2), if someone is curious. Maybe the problem is that I execute from CanvasLayer? Not sure…

extends CanvasLayer
class_name UnlockMenu

const UI_BUTTON = preload("res://ui/button/ui_button.tscn")
@onready var hbox_container: HBoxContainer = %hbox_container


func init(unlocks : Dictionary) -> void:
	for i : int in unlocks.size():
		# get unlock name
		var unlock_scene : PackedScene = unlocks.values()[i]
		var unlock_instance : Node = unlock_scene.instantiate()
		var unlock_name : String = unlock_instance.get_stats().name

		# add button
		var new_button : Button = UI_BUTTON.instantiate()
		hbox_container.add_child(new_button)
		new_button.text = "Unlock " + unlock_name
		new_button.custom_minimum_size.y = 50
		
		if unlock_instance is Weapon:
			new_button.pressed.connect(func() -> void: EventManager.weapon_unlocked.emit(unlock_scene))
		elif unlock_instance is EnemySpawner:
			new_button.pressed.connect(func() -> void: EventManager.enemy_unlocked.emit(unlock_scene))

		new_button.pressed.connect(func() -> void: EventManager.unlock_chosen.emit())
		new_button.pressed.connect(queue_free) # close unlock menu

		unlock_instance.queue_free()

Does the workaround mean that it works?
I didn’t know it was possible to bind it internally inside a .connect() like that.
I usually write it like this

if unlock_instance is Weapon:
    new_button.pressed.connect(func(): EventManager.weapon_unlocked.emit(unlock_scene))
1 Like

Yes, the workaround works. The binding is very useful, here a reddit post where people discovered it and are very happy: https://www.reddit.com/r/godot/comments/191w2v0/just_learned_that_you_can_bind_arguments_to/

If it works when launching it from the editor but not on an exported build then it’s probably an engine bug. Please report it here Issues · godotengine/godot · GitHub

Weird…I can not repro this in a freshly made project. Seems to be an issue with my game project then…

Being able to use Variant built-in methods as Callables was added in Godot 4.3 Core: Allow methods of built-in `Variant` types to be used as Callables by dalexeev · Pull Request #82264 · godotengine/godot · GitHub Previous versions didn’t let you do what you are doing.

The only way I can think of where you can use an older version of the engine in an export template is if you are exporting to Android using Gradle with the Android build template. If that’s the case you’ll need to update it to the latest version.

My project is 4.3
I started as 4.2 but then opened it in 4.3 and let it convert. Do you think that’s the problem? That the conversion didn’t work?

If you are using Gradle with the Android build template then, yes. I don’t think the build template updates automatically but I’m not sure.

If it should and did not work then that’s a bug that should be reported.

I’m not building for Android. Just Windows at the moment.
But when I installed and started 4.3, I had to load 1 GB of build templates. So I think it’s all good on this side.
Also, with a fresh project I can’t repro the issue. So there must be something in my converted 4.2 project which breaks it…if I find it, I will report it here.