Always Nonexistent signal on Debugger

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

I want to add a function where when the Player is pressed that the Enemies disappear unill the next level loads

This is my Main Script


func _on_player1_pressed():
         Enemy2.visible = false

Invalid set index ‘visible’ (on base: ‘Array’) with value of type ‘bool’

Whenever i try to make this Enemy2 invisible some error with Array happens.

So i tried to use signals

signal next_level

var Enemy2 = [
preload("res://Enemy/Enemy1.tscn"),
preload("res://Enemy/Enemy3.tscn"),
preload("res://Enemy/Enemy4.tscn")
]

func _on_player1_pressed():
       emit_signal("next_level")

Enemy Script:

func  _ready() -> void:
      $Enemy2sprite.connect("next_level", self, "_on_next_level")


#Option 1

func _on_next_level():
     $Enemy2sprite.visible = false or .queue_free() or .remove() or .erase() or .visible = false  #i've tried them all



#Option 2

func _on_next_level():
    $Tween.interpolate_property($Enemy2sprite, "modulate:a", 1, 0, 1)
    $Tween.start()


#Option 3
func hide_for_time(t):
    visible = false
    yield(get_tree().create_timer(t), "timeout")
    visible = true

func _on_next_level():
    get_node("Enemy2sprite").hide_for_time(1.0)

#Always Nonexistent signal ‘next_level’ on method Something.on_next_level?
This Something was a Sprite, Node2D, RigidBody2D i’ve tried them all

I can’t tell from your code what is wrong, but it looks like you might be getting a bit mixed up with the types of object. You always need to be sure about the type of object which a variable points to.

For example in this text

var Enemy2 = [
preload("res://Enemy/Enemy1.tscn"),
preload("res://Enemy/Enemy3.tscn"),
preload("res://Enemy/Enemy4.tscn")
]

You have defined Enemy2 as an array. The array contains resource_path objects.

In this example, you are trying to call a method on that array

func _on_player1_pressed():
         Enemy2.visible = false

If you have defined Enemy2 is an array you can only call methods or set properties which arrays or their parents have. An array does not have a .visible property so you get an error.

In the rest of your examples, you are using the format “$Enemy2sprite” - in this format, the script looks for a Node which is a child of the node where the script is running called Enemy2sprite. If Enemy2sprite is not a direct child of the node where the script is, you will get an error. Also, if Enemy2sprite is a type of node which does not have the method you are calling, you will get an error. You can use that format to make more complicated paths, for example $“/root/World/Enemy2sprite” to reach nodes, but usually that gets complicated quickly.

Here are a couple of things you could try

  1. Define an array which includes references to all enemy instances (not the resource_path but an instance of the scene). Every time you create an Enemy, you can add a reference to them into that array. Then when you need to act on enemies you can loop through the array or pick one element from the array and then call methods against that object
    or
  2. Consider putting all your enemy instances in a Group. This is like a tag which you can add to Nodes. When you have a Group you can use methods like this
    get_tree().call_group(“Enemies”, “next_level”)
    This will find all nodes in the group Enemies and then call the method next_level on each of those nodes. This will probably be easier than option 1

AndyCampbell | 2020-11-23 12:48

var Enemy1Instance = Enemy1.instance()
Enemy1Instance.visible = false or .remove() or .erase() or .queue_free()

i don’t understand how can it give this error:

Invalid call. Nonexistent function ‘instance’ in base ‘Array’.

how is that possible it boggles my mind

Rayu | 2020-11-23 13:15

How have you defined Enemy1?

In that example, you try to call .instance() against Enemy1 - and it looks like Enemy1 is an array. An array does not have an .instance() method. You need to ensure Enemy1 points to a definition of a scene (not the top level of an array holding definitions). In your first example, I can see you were storing your scene paths inside an array.

If you try this it might work (I dont have access to test this right now)

var Enemy1 = preload("res://Enemy/Enemy1.tscn") 
var Enemy1Instance = Enemy1.instance()   # This makes a new instance of the scene
add_child(Enemy1Instance) 
Enemy1Instance.visible = false or .remove() or .erase() or .queue_free()

AndyCampbell | 2020-11-23 13:37

var Enemy1ins = preload("res://Enemy/Enemy1.tscn")

var Enemy1instance = Enemy1ins.instance()

func _on_player1_pressed():
     add_child(Enemy1instance)
     Enemy1isntance.visible = false

nothing happens in the debugger just ignores it

Maybe (idk) it has something to do with this
because the Enemy is called here
and i want basically to remove the spawn_enemy1() function

func _ready() -> void:
	var actor_id = randi() % 4 + 1
	if actor_id == 1:
		spawn_player1(1)
		for _i in range(4 + Globals.Score):
			spawn_enemy1()

func spawn_enemy1():
	var rand1 = floor(rand_range(0, Enemy1.size()))
	var piece1 = Enemy1[rand1].instance()
	add_child(piece1)

Rayu | 2020-11-23 15:02

Hi

When you add an instance to the game, you then need to call add_child before it will show up in the world. Until you do this, the instance exists in your code and you can make changes to prepare it but it won’t be active the game. In your first example you only add_child when _on_player1_pressed and then immediately make it invisible again. So, I guess that’s why you don’t see it. It looks like you got it right in the spawn_enemy1() code

I am getting confused with all the small pieces of code that you are sending - it is hard to know what code is in what script.

In this example code which you shared:

var Enemy1ins = preload("res://Enemy/Enemy1.tscn")

var Enemy1instance = Enemy1ins.instance()

func _on_player1_pressed():
     add_child(Enemy1instance)
     Enemy1isntance.visible = false

if you want to make that work I believe you need to…

  1. Make sure these are at the top of your script before the function definitions
var Enemy1instance    # This needs to be a member variable (declared outside a function) so it persists and can be used in all functions.
var Enemy1ins = preload("res://Enemy/Enemy1.tscn")
  1. Ensure you are including add_child for the instances. You could add this to your _ready
func _ready()

    Enemy1instance = Enemy1ins.instance()
    add_child(Enemy1instance)

If you run that, the Enemy should show up. If it does not, something else is wrong which we need to fix first.

Assuming that works, then you can try

func _on_player1_pressed():
     Enemy1instance.visible = false

However… if you have a bunch of similar enemies, like you just shared in your last example with spawn_enemy1 it’s probably going to be easiest to use a Group. To do this, ensure your code is correctly showing Enemies, then add your Enemy scene(s) to a Group, maybe called Enemy. You can do that in the editor (no code required) - instructions here

When you have a group, and you are successfully showing enemies on screen then you can easily call functions on every member of the group like this

In your enemy script, try this sample function (you might already have this from a previous question)

func hide_for_time(time):
	visible = false
	yield(get_tree().create_timer(time), "timeout")
	visible = true

This makes the enemies invisible, then waits for the duration of the time variable, then makes them visible again

In your player script, add

func _on_player1_pressed():
	get_tree().call_group("Enemy", "hide_for_time", 2)

This gets every member of the “Enemy” Group and calls a function called “hide_for_time” on the every member. The function is called with the parameter “2” (duration).

AndyCampbell | 2020-11-23 23:53

OOOOOOOOOOOOOOOOHHHHHHHHHHHHHHHHHHHHH YYYYYYYYYYYYYEEEEEEEEEEAAAAAAAAAHHHHHHHHHHHHH

Thank you very much

Rayu | 2020-11-24 10:37