I don't think signals are working

Godot Version

4.5.1

Question

Hi! I’m very new to godot and game dev in general, though I have done a wee bit of coding in R (mostly statistics and linear models).

I was trying to follow this tutorial to make an NPC that can move and talk to the player:

Here is most of the code:

extends CharacterBody2D

signal chatting

@export var speed = 30 # sets start state and gives move speed
var current_state = IDLE

var dir = Vector2.RIGHT
var start_pos

var is_roaming = true # makes the character move to begin with rather than being in chatting state
var is_chatting = false

var player
var player_in_chat_zone = false # to begin with, player is not in the chatting zone

enum {
	IDLE,
	NEW_DIR, # Adds states for the NPC to be in
	MOVE
}

func _ready():
	randomize()
	start_pos = position # use this to restrict where the NPC goes
func _process(delta): # this says what to do in each state
	if current_state == 0: # don't do anything in idle
		$AnimatedSprite2D.play("default")
		pass
	elif current_state == 1: # changes direction and sprite
		dir = choose([Vector2.RIGHT, Vector2.UP, Vector2.LEFT, Vector2.DOWN])
		if dir.x == -1:
			$AnimatedSprite2D.play("left_idle")
		if dir.x == 1:
			$AnimatedSprite2D.play("right_idle")
		if dir.y == -1:
			$AnimatedSprite2D.play("up_idle")
		if dir.y == 1:
			$AnimatedSprite2D.play("default")
	elif current_state == 2 and !is_chatting: # move based on direction and if not chatting
		if dir.x == -1:
			$AnimatedSprite2D.play("left_idle")
			move(delta)
		if dir.x == 1:
			$AnimatedSprite2D.play("right_idle")
			move(delta)
		if dir.y == -1:
			$AnimatedSprite2D.play("up_idle")
			move(delta)
		if dir.y == 1:
			$AnimatedSprite2D.play("default")
			move(delta)
	
	if Input.is_action_just_pressed("Chat") and player_in_chat_zone == true:
		print("chatting with NPC")
		$Dialogue.start()
		is_roaming = false
		is_chatting = true
		$AnimatedSprite2D.play("default")
	
func choose(array): # shuffles what option is chosen and chooses one from an array
	array.shuffle()
	return array.front()
		
func move(delta): #makes it move if not having a chat
	if !is_chatting:
		position += dir * speed * delta
			
# for chat detection
func _on_chat_detection_zone_body_entered(body):
	if body.has_method("player"):
		player = body
		player_in_chat_zone = true
		
func _on_chat_detection_zone_body_exited(body):
	if body.has_method("player"):
		player_in_chat_zone = false
		is_chatting = false

# when the timer runs out, change the state
func _on_timer_timeout():
	current_state = choose([IDLE, NEW_DIR, MOVE])
	print(current_state)

# stop talking when it's over
func _on_dialogue_dialogue_finished():
	is_chatting = false
	is_roaming = true

I have three main issues with this code:

  1. The NPC doesn’t cycle through its states
  2. The chat detection zone doesn’t detect the presence of the player
  3. The dialogue doesn’t finish at the end.

All three of these are connected to the signals to and from other nodes, which makes me think that these signals just aren’t being emitted. The states definitely work (I can make it permanently any one of the three states and the part starting func _process(delta):also seems to work). Any help is appreciated!

(I realise there may be another problem with the sprites, but I’ll deal with that later haha)

I don’t see any chatting.emit() or chatting.connect() statements in the code. Are these forgotten or somewhere else? You need to emit a signal and soemthing needs to listen to it via the connect.

I think two of the signals are connected to other nodes attached to the CharacterBody2D: Timer and an Area2D (Chat_detection_zone). I believe these are connected with the func on_chat_detection_zone_body_entered(body):and func ontimer_timeout():I think the dialogue finished signal comes from the dialogue script. Should I have those other elements you mentioned as well?

Are you connecting the signals via the UI? A screenshot of your character’s signals panel in the editor would be helpful to see what signals you’re using and where they connect.

1 Like
  1. On the line func on_timer_timeout is there a green arrow going through a square to the left, inside the margin of the script?

If you could post a picture of your script window and scene tree where all these signals are supposed to be working it would help out a lot, but I don’t know the window when new users are able to upload pictures.

  1. As for the area not detecting the player, you have if body.has_method(“player”). That asks the engine to read through the bodies script and find any function that contains the function name player. I haven’t seen the tutorial but I can’t really imagine there would be a function declared func player() → void: or something, which is why the code following that if doesn’t work.

  2. I believe the same issue is happening as case 1. Check if there is a little green arrow going through a rectangle in the margin to the left of the line on_dialogue_finished

1 Like

Thanks for the info on 2., that sounds like the reason why it’s not working, could you suggest a way to adjust it?

As for the other two, here are the screenshots:

It seems to have the arrows you’re referring to, unless I’m missing something?

Sure, instead of checking a method, you can check body.name == my_node_name, where my_node_name is whatever you typed to replace the default mode name when it is added to the scene tree. (Node2D, CharacterBody2D etc.)

Those are the symbols I was talking about. So that means the signals are in fact connected and will work when their conditions are met (either built in or called by using signal_name.emit(args)).

The only thing I can think of for the timer is that the properties aren’t set up correctly. If you click on the timer and look at the menu on the right, is Autostart checked or unchecked? Is the wait time greater than zero? Is One Shot checked or unchecked?

I believe you should have Autostart checked, I would set wait time to maybe 2.5 or something, and One Shot should be unchecked.

Thank you! I managed to solve the problem

Cool. Glad I could help. How did you get the chatting signal to work?

It’s not pretty, but I just left it as:

func _on_chat_detection_zone_body_entered(body):

player_in_chat_zone = true

When this gets bigger and more complicated, I’ll probably have to readjust it again, but it’ll do for now. Hopefully by then I’ll be more practiced and knowledgeable