Signal not working !!!

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By javrocks

I have it code that is supposed to send an signal from a Kinematic Body in my Enemy scene my World Scene

When the enemy dies i want it to spawn a new enemy
But the replace function doesn’t fire nor does it print (“replace”)

extends KinematicBody2D   #ENEMY SCENE

signal spawn
var health = 100

func _physics_process(delta):

  if health <= 0:
      emit_signal("spawn")
      queue_free()


extends Node2D    #WORLD SCENE

onready var Enemy = get_tree().get_root().find_node("Enemy", true, false)


func ready():

    Enemy.connect("spawn",self,"replace")


func replace():
   
    print("replaced")
    spawn_enemy()

Hello,
I answered, but read your code wrong… Will try again :slight_smile:

deaton64 | 2021-12-31 13:07

I removed queue_free() but the signal still dosent fire
I think for some reason my Enemy is null even though
onready var Enemy = get_tree().get_root().find_node(“Enemy”, true, false)
has worked before

javrocks | 2021-12-31 13:12

right OK, check case of filenames etc…

Here’s the code working: Download me

There’s a timer in the enemy that sets the health to -1 after 2 seconds.

deaton64 | 2022-01-02 11:13

:bust_in_silhouette: Reply From: deaton64

Hi,

Found it… maybe

func ready(): should be func _ready():

You can put the queue_free() back as well.

It says attempt to call function “connect” in base null on a null instance

javrocks | 2021-12-31 14:17

OK, so I created 2 scenes, a world scene with this script:

extends Node2D    #WORLD SCENE

    onready var Enemy = get_tree().get_root().find_node("Enemy", true, false)
    
    func _ready():
    	print("World ready")
    	Enemy.connect("spawn",self,"replace")
    	print(Enemy)
    
    func replace():
    	print("replaced")
    	spawn_enemy()
    
    func spawn_enemy():
    	print("New enemy")

and an Enemy scene with this script:

extends KinematicBody2D   #ENEMY SCENE

signal spawn
var health = 100

func _ready() -> void:
	$Timer.start()

func _physics_process(delta):

	if health <= 0:
		emit_signal("spawn")
		print("Dead")
		queue_free()

func _on_Timer_timeout() -> void:
	health = -1

I have a 1 second timer node in the Enemy scene and it does what you want it to do.

What do you get if you add these bits:

func _ready():
	print("World ready")
	Enemy.connect("spawn",self,"replace")
	print(Enemy)

Any chance you could upload your project?

deaton64 | 2021-12-31 15:08

When i tell it to print the player it prints me the correct node but
when i try to print Enemy it tells me the object is null which is why its not wrk
i can’t figure out why it would be null when the object exists

extends KinematicBody2D

onready var player = get_tree().get_root().find_node("Player", true, false)

onready var Enemy = get_tree().get_root().find_node("Enemy", true, false)


func _ready():
       
    print(Enemy)
    print(player)

javrocks | 2021-12-31 15:29

Is your Enemy scene name definitely Enemy with a capital E and in the works scene?

Maybe create a copy of your project and delete anything you don’t want anyone to see and upload that.

deaton64 | 2021-12-31 15:45

Ok i draged the Enemy scene into my World scene and it seems to pick it up now
Thanks

javrocks | 2021-12-31 15:59

Jolly good …….

deaton64 | 2021-12-31 16:06

Actually i relised that the only thing that’s working properly is that Enemy that i draged into
my World scene
When i kill that Enemy specifically it replaces itself
But when i spawn_enemy() in the ready function and kill it, it doesn’t replace itself

javrocks | 2021-12-31 23:31

The spawn enemy function needs to be in the world scene, not the enemy scene.
You destroy the enemy, so it can’t respawn

See my code above. .

deaton64 | 2022-01-01 10:19

the spawn function is in the world scene but if i spawnan enemy it dosen’t wrk
only the enemy that’s there because i draged in my enemy scene as a child of the world replaces itself

javrocks | 2022-01-01 13:03

Can you post your spawn code?

deaton64 | 2022-01-01 13:44

extends Node2D


export(PackedScene) var Enemy_Scene: PackedScene = preload("res://Scenes/Enemy.tscn")

var player_pos = Vector2()
var spawning_area = Rect2()
var num = 1

const Width =  2016
const Height = 1160
const safe_range = 900  #define this as the minimum distance from the player


onready var player = get_tree().get_root().find_node("Player", true, false)


func _ready():

num = 1
spawning_area = Rect2(0,0,Width,Height)


func spawn_enemy():
   for i in range(0,num):
	  var position = _get_new_spawn_position()
	  var New_Animal_Scene = Enemy_Scene.instance()
	  add_child(New_Enemy_Scene)
	  New_Enemy_Scene.position = position
	
	
func _get_new_spawn_position() -> Vector2:
  randomize()
       var spawn_pos: = Vector2(randi()%Width,randi()%Height)
       if abs(spawn_pos.x - player_pos.x) < safe_range: #  too close
	      spawn_pos = _get_new_spawn_position() # just get a new one instead
       print (spawn_pos.x)
       return spawn_pos

javrocks | 2022-01-01 15:08

:bust_in_silhouette: Reply From: deaton64

Hi,

It started to get a bit messy when I replied to your comments, so I’ve shoved it in a new answer.
So… I’ve based this on the enemy script you posted above and added the following to the last script you posted:

  1. In the _ready() function, call the spawn_enemy() function to get an enemy in the world.
  2. Add the replace() function, that your original script sends a signal to.
  3. In the spawn_enemy() function, add the signal connection.
  4. Replace New_Animal_Scene with New_Enemy_Scene in the spawn_enemy() function.

That works on my system.

extends Node2D    #WORLD SCENE

export(PackedScene) var Enemy_Scene: PackedScene = preload("res://Enemy.tscn")

var player_pos = Vector2()
var spawning_area = Rect2()
var num = 1

const Width =  2016
const Height = 1160
const safe_range = 900  #define this as the minimum distance from the player

onready var player = get_tree().get_root().find_node("Player", true, false)

func _ready():
	num = 1
	spawning_area = Rect2(0,0,Width,Height)
	spawn_enemy()

func replace():
	spawn_enemy()

func spawn_enemy():
	for i in range(0,num):
		var position = _get_new_spawn_position()
		var New_Enemy_Scene = Enemy_Scene.instance()
		New_Enemy_Scene.connect("spawn",self,"replace")
		add_child(New_Enemy_Scene)
		New_Enemy_Scene.position = position

func _get_new_spawn_position() -> Vector2:
	randomize()
	print("getting position")
	var spawn_pos: = Vector2(randi()%Width,randi()%Height)
	if abs(spawn_pos.x - player_pos.x) < safe_range: #  too close
		spawn_pos = _get_new_spawn_position() # just get a new one instead
	print (spawn_pos.x)
	return spawn_pos

I added everything but nothin happened
The replace function didn’t fire
Do u have Enemy = get_tree().get_root().find_node(“Enemy”, true, false) in the ready function or is it somewhere else

javrocks | 2022-01-01 22:10

Did u reply to my email because i don’t see the comment here

javrocks | 2022-01-02 13:49

No, I replied further up… but this is what I put…

right OK, check case of filenames etc…

Here’s the code working: Download me

There’s a timer in the enemy that sets the health to -1 after 2 seconds.

deaton64 | 2022-01-02 14:18

Does wettransfer costs money
If i were to send it
How would i send without wettransfer

javrocks | 2022-01-02 21:09

We transfer is free up to 2GB.
Did you try what I sent?

deaton64 | 2022-01-02 21:18

What u have works but i don’t use a timer i use collisions to reduce the health
If u run this WeTransfer - Send Large Files & Share Photos Online - Up to 2GB Free
2 enemy’s will spawn and if u get close to them they will chase the player and die
Then nothing happens after that
It helps to turn on visible collision shapes in Debug

javrocks | 2022-01-02 21:43

yes, I used a timer as I didn’t want to write a load of code to match your game, I just needed to test the theory.
Anyway, I’ve worked out what’s going on now I’ve seen your project. That’s the problem with a lot of these things, you can’t work it out until you see the whole project.

I’ll try and explain it the best I can. I’m no expert on this.

If you look at my project, you can see that the root node of my Enemy is a KinematicBody2D and I connect the signal to that node and the script to emit the signal is also connected to that node.

In your project, the root node is a Node2D, with a child node that is a KineticBody2D with the script attached to that.
Your World script, instances a new enemy scene and connects a signal to that root node, but the script that emits the signal is attached to the child node, so the signal doesn’t work.

As I said, I’m no expert, but I think you have three options.

  1. Use the code below, or something similar to it, so that you connect the signal to the child node that has the code attached to it.
  2. Move the script to the root node and refactor it to work from there.
  3. Change the Enemy so the root node is a KineticBody2D.

Personally I’d do option 3. In my projects, I always/usually use the main type of thing I’m doing as the root node.

Here’s the code that should work, but you will get stack overflow from your _get_new_spawn_position() function calling itself over and over again, so I’ve changed that so it doesn’t crash.

func spawn_enemy():
	for i in range(0,num):
		var position = _get_new_spawn_position()
		var New_Enemy_Scene = Enemy_Scene.instance()
		add_child(New_Enemy_Scene)
		# find the Enemy KineticBody2D node
		var Enemy_child = New_Enemy_Scene.find_node("Enemy", true, false)
		print("child = ", Enemy_child)	# you will see this node is different to the New_Enemy_Scene
		# connect the signal to the child node
		Enemy_child.connect("spawn",self,"replace")
		New_Enemy_Scene.position = position
		print ("Enemy is ", New_Enemy_Scene)
		
func _get_new_spawn_position() -> Vector2:
	randomize()
	var spawn_pos: = Vector2(randi()%Width,randi()%Height)
	if abs(spawn_pos.x - player_pos.x) < safe_range: # if we are too close
		spawn_pos.x = safe_range + (safe_range - spawn_pos.x)
	#print (spawn_pos.x)
	return spawn_pos

deaton64 | 2022-01-03 12:15

I used the code and it seems to be working
Thanks for all ur help!!
Any tips for how to make the state machine in my enemy script better
I needed it to output 1 random direction in the array but the choose function keeps cycling through all of them

func choose(array):
   array.shuffle()
   return array.front()

 

javrocks | 2022-01-03 15:00

You’re welcome. It took a while to get there.

I’d consider using Enum’s to set the state value. That way, if you get a lot of states, you won’t have to remember which number is which state.

Put this in the vars section: enum State {IDLE, RUN, WALK, JUMP, FALL}
This will make IDLE = 0, RUN =1, etc…
Then you can do things like enemy_state = State.WALK and if enemy_state == State.JUMP

array.shuffle() uses the global random number generator, so shove a randomize() in front of it.

deaton64 | 2022-01-03 17:16