How to emit a signal from one script to another to say a var is ready (Invalid call. Nonexistent function 'set_text' in base 'Nil')

Godot Version

4.1.3

Question

I’m very new to Godot and am working on an fps game. I was following this tutorial. There is a “weapon_manager” script that handles switching and firing weapons, and a “HUD” script that’s part of a canvas layer that is supposed to display the current weapon, current ammo, and weapons currently being held. However, when I run the project I get the error “Invalid call. Nonexistent function ‘set_text’ in base ‘Nil’.” for this line of code in the HUD script:

# Update Weapon Stack
func _on_weapons_manager_update_weapon_stack(Weapon_Stack):
	-> **CurrentWeaponStack.set_text("")**  <-
	for i in Weapon_Stack:
		CurrentWeaponStack.text += "\n"+i

I checked the comments on the tutorial and some others were having the same issue. One comment said they found a solution: “The weapon manager was calling the HUD script before it was initialized. I fixed it by emitting a signal from the HUD to the weapon manager to say it was ready.” I tried looking into how signals work but I’m having a hard time understanding how it works.

Any help on this would be greatly appreciated. I’ll leave the code for HUD.gd and weapons_manager.gd below. Let me know if you need any other details on this.

HUD.gd

extends CanvasLayer

@onready var CurrentWeaponLabel = $VBoxContainer/HBoxContainer/CurrentWeapon
@onready var CurrentAmmoLabel = $VBoxContainer/HBoxContainer2/CurrentAmmo
@onready var CurrentWeaponStack = $VBoxContainer/HBoxContainer3/WeaponStack

# Update Current Ammo
func _on_weapons_manager_update_ammo(Ammo):
	CurrentAmmoLabel.set_text(str(Ammo[0]))

# Update Weapon Stack
func _on_weapons_manager_update_weapon_stack(Weapon_Stack):
	CurrentWeaponStack.set_text("")
	for i in Weapon_Stack:
		CurrentWeaponStack.text += "\n"+i

# Update Weapon Label
func _on_weapons_manager_weapon_changed(Weapon_Name):
	CurrentWeaponLabel.set_text(Weapon_Name)

weapons_manager.gd

extends Node3D

signal Weapon_Changed
signal Update_Ammo
signal Update_Weapon_Stack

@onready var Animation_Player = get_node("%WeaponAnimations")

var Current_Weapon = null

var Weapon_Stack = [] # An Array of Weapons Currently Held by the Player

var Weapon_Indicator = 0

var Next_Weapon: String

var Weapon_List = {}

@export var _weapon_resources: Array[Weapon_Resources]

@export var Start_Weapons: Array[String]

func _ready():
	Initialize(Start_Weapons) # Enter the state machine

# Take Inputs
func _input(event):
	if event.is_action_pressed("weapon_up"):
		Weapon_Indicator = min(Weapon_Indicator+1, Weapon_Stack.size()-1)
		exit(Weapon_Stack[Weapon_Indicator])
	
	if event.is_action_pressed("weapon_down"):
		Weapon_Indicator = max(Weapon_Indicator-1, 0)
		exit(Weapon_Stack[Weapon_Indicator])
	
	if event.is_action_pressed("shoot"):
		shoot()

func Initialize(_start_weapons: Array):
	# Creating a Dictionary to refer to our weapons
	for weapon in _weapon_resources:
		Weapon_List[weapon.Weapon_Name] = weapon
	
	# Add start weapons
	for i in _start_weapons:
		Weapon_Stack.push_back(i)
	
	# Set the first weapon in the stack to current
	Current_Weapon = Weapon_List[Weapon_Stack[0]]
	emit_signal("Update_Weapon_Stack", Weapon_Stack)
	enter()

# Call when first entering into a weapon
func enter():
	Animation_Player.queue(Current_Weapon.Activate_Animation)
	emit_signal("Weapon_Changed", Current_Weapon.Weapon_Name)
	emit_signal("Update_Ammo", Current_Weapon.Current_Ammo)

func exit(_next_weapon: String):
	# Check if next weapon is not the current weapon
	if _next_weapon != Current_Weapon.Weapon_Name:
		# Check if weapon change animation is currently playing
		if Animation_Player.get_current_animation() != Current_Weapon.Deactivate_Animation:
			Animation_Player.play(Current_Weapon.Deactivate_Animation)
			Next_Weapon = _next_weapon

# Change current weapon to new weapon
func Change_Weapon(weapon_name: String):
	var Weapon_Index = Weapon_Stack.find(weapon_name)
	if Weapon_Index != -1:
		Current_Weapon = Weapon_List[Weapon_Stack[Weapon_Index]]
		Next_Weapon = ""
		enter()

# Change Weapon once deactivate weapon animation has finished
func _on_weapon_animations_animation_finished(anim_name):
	if anim_name == Current_Weapon.Deactivate_Animation:
		Change_Weapon(Next_Weapon)

func shoot():
	Animation_Player.play(Current_Weapon.Shoot_Animation)

Have you read the documentation and understood how the ‘signals’ can be connected?

your scene tree also looking like this?
image

basically it means the CurrentWeaponStack doesnt exist from specified path

Like I mentioned I looked into how signals work (including the documentation) but am having a hard time understanding how it should be used in this specific case.

image

This is what I have right now

who call this method to run? it’s from editor connected?
check if it’s connected from the correct signal

is it right away it error?, then the node might not be ready yet to be called

change to this code for initialize

func Initialize(_start_weapons: Array):
	# Creating a Dictionary to refer to our weapons
	for weapon in _weapon_resources:
		Weapon_List[weapon.Weapon_Name] = weapon
	
	# Add start weapons
	for i in _start_weapons:
		Weapon_Stack.push_back(i)

	await get_tree().process_frame
	# Set the first weapon in the stack to current
	Current_Weapon = Weapon_List[Weapon_Stack[0]]
	emit_signal("Update_Weapon_Stack", Weapon_Stack)
	enter()
1 Like

Adding this line of code for initialize seems to have resolved the error:

await get_tree().process_frame

Does this line of code just wait until all other processes are done in the current frame before continuing?

Unfortunately, now I’m getting a new error “Invalid get index ‘0’ (on base: ‘int’)” for this bit of code in HUD.gd

# Update Current Ammo
func _on_weapons_manager_update_ammo(Ammo):
	CurrentAmmoLabel.set_text(str(Ammo[0]))

I’m not seeing any other issues that would cause an error like this. This error occurs immediately when I start the project.

you can read here to what it do:

basically you wait for all nodes to be ready before proceeding to next line of code, so it should guarantee the node called to be ready and exist

1 Like

on the enter function change it to:

func enter():
	Animation_Player.queue(Current_Weapon.Activate_Animation)
	emit_signal("Weapon_Changed", Current_Weapon.Weapon_Name)
	emit_signal("Update_Ammo", [Current_Weapon.Current_Ammo])

This fixed that error. Did I need to add brackets because I’m working with an integer? Either way, thank you so much for the help!

it’s actually not an integer, the [ ] is simply creating Array of int that’s Current_Weapon.Current_Ammo’s value

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.