|
|
|
|
Reply From: |
AlexTheRegent |
Yes, signals are attached to all instances of instanced scenes. But signal connections are instance-based. So if you have scene (enemy
) with signal, one instance (enemy1
) that is connected via code to another scene (player
) and second instance of enemy
scene (enemy2
) without connection to player
. Then, when enemy2
will emit signal, player
will not receive it, because it does not listen for signals from enemy2
.
Then, when enemy2
will emit signal, player
will not receive it,
because it does not listen for signals from enemy2
.
Exactly this, so how how do I do that? Is it possible, because emission of signal from the many instances of a scene created through code during runtime is needed for expected functioning in my case. How do I do this, if possible?
OR
How do I make it such that every instance of the original/ connected scene automatically has connection to the scene
Optimal solution is depend on your situation. If you have multiple enemies and one player, you can modify _ready()
function of Enemy
to automatically connect signals to player.
connect("hit", get_tree().root.find_node("Player"), "on_enemy_hit", [self])
Or if all signal’s calls must be received by one node (i.e. “many-to-one” relation), you can do it without signals, by calling function directly, something like this:
# emit_signal("hit", player) # this can be done by calling method on player
get_tree().root.find_node("Player").on_enemy_hit(self)
If you have many players and many enemies, you can use groups to notify one group. When you instance scene, you add it to group using add_to_group
. When you need to notify all nodes in group, you use get_tree().call_group
method.
And there are many other solutions. But, as I already said, optimal solution is depend on your situation.
AlexTheRegent | 2021-01-06 15:07
Let me explain again, I think that’s not the error for not getting expected result or maybe it is, I’m confused.
So, I have a Mainscene.tscn
and a coin.tscn
.
I have instanced coin scene by once dragging and dropping it into the Scenetree from the Filesystem.
Then, I have made it so that the res://coin.tscn
is instanced over (above) every tile I have for my player to land on/interact with.
The manually instanced coin, I’ve used that to edit and connect signal connections to the game’s main script.
In the coin.tscn
’s script I’ve made my custom signal for when player’s kinematic body will enter coin’s area2D.
That is connected to mainscript - main.gd
’s receiver method
(say, func _on_receive():
)
I also have a variable for score
initiated 0, which is set to increase by +1 in the receiver method of mainscript.
Now, what I want is that the coins instanced through code should also behave the same as the one coin manually instanced.
But the problem is that when my player enters the manually instanced coin’s area it increases score by +1 but never does this when player enters areas those of its instances created via code.
I tried to check whether or not the signal was being emitted when player enter the coin instanced created through code by adding a print(collected)
statement. And that is printed errorlessly, equal times as there are coins collected.
So, that’s why I think there’s not the signal problem. Just the score doesn’t increase, it turns 1 when manually instanced coin is collected and never increases again when coins instanced through code are collected.
Scenetree:
Mainscene.tscn → main.gd → receive signal “collected” and then increase score = score + 1
^
| coin scene instanced to mainscene
|
coin.tscn and all instances → coin.gd → emit signal “collected”
I hope I explained the ‘My Case’ deep and better.
I can post you the code snippets if you want.
You can add function to main.gd
to spawn coin and connect it to signal:
func spawn_coin(position):
var coin = load("path/to/coin.tscn").instance()
coin.connect("collected", this, "_on_receive")
add_child(coin) # add_child to node where you keep your coins
This way all created coins by this function will be automatically connected to your main.gd
_on_receive
method.
AlexTheRegent | 2021-01-07 08:58
Cool, that works fine. But I had to replace this
with self
'cuz it said this
is not declared in the current scope. Other than that it all works fine.
But I have an array consisting of two types of collectibles, that spawn randomly and each’s score is independently saved. And I use a randi()
to randomly pick a type of collectible.
So, your code works on only if there is one collectible. How would I do if it were two or say more.
(Sorry, if I sound too kiddish but I’m new to game dev in general )
Thanks for your help @AlexTheRegent.
Yes, this
should be self
, I’m so used to C++ and I’m writing examples outside of editor, so my snippets may contain small errors.
You can add second argument to your function as type of coin. Then you can use this variable to select required scene and callback.
func spawn_coin(position, type):
var coin
if type == 0:
coin = load("path/to/silver/coin.tscn").instance()
coin.connect("collected", self, "_on_silver_coin_collected")
elif type == 1:
coin = load("path/to/gold/coin.tscn").instance()
coin.connect("collected", self, "_on_gold_coin_collected")
coin.global_position = position
$Coins.add_child(coin) # add_child to node where you keep your coins
Note that this approach is used when you have manageable amount of similar objects. If you will make something like different usable items, this function will grow quickly and it will be hard to maintain it. To solve this problem, config files with path to item’s init scripts might used, instead of hardcoding all objects into one function. But in your case with coins hardcoding approach will work just fine.
AlexTheRegent | 2021-01-07 12:40
Actual code snippets from my project :
func coinspawner():
var rcoll = (collectibles[randi()%collectibles.size()]).instance()
if rcoll == collectibles[0].instance():
rcoll.connect("coin_collected", self, "_on_coin_collected")
else:
print("carrot collected !")
rcoll.connect("coin2_collected", self, "_on_coin2_collected")
Now, my problem is that the condition if rcoll == collectibles[0].instance():
is not being evaluated. Just the else part is executed straightaway. Why? Or is the entire code wrong or messed up? IJDK ¯\(ツ)/¯
func _on_coin_collected():
coinscore = coinscore + 1
coinl.text = str(coinscore)
func _on_coin2_collected():
coin2score = coin2score + 1
coin2l.text = str(coin2score)
coinscore
and coin2core
are variables initiated 0
and collectibles
is an array as I said in my earlier post = [coin, coin2]
, where
var coin = load("res://assets/coin.tscn")
var coin2 = load("res://assets/coin2.tscn")
all at the beginning of the file.
Now that you know the exact situation, Any help would be greatly appreciated !
There is a big difference between scenes and instances. Imagine that you like Apples
(scene
in current context). This is just word, that describes whole class of apples. Now when you instancing an instance, in words of apples you take one particular apple Apple1
from basket. And when you instancing second apple, you get Apple2
. It is obvious that Apple1
can not be Apple2
at the same time (you already removed Apple1
from basket, you can’t get it again from basket). This is reason why your condition always false. You need to compare either scenes or primitive values (strings, floats, ints, etc), but not instances (they are always different). So you can fix your code by next changes:
func coinspawner():
var rcoll = (collectibles[randi()%collectibles.size()])
var coin = rcoll.instance()
if rcoll == collectibles[0]:
coin.connect("coin_collected", self, "_on_coin_collected")
else:
print("carrot spawned!")
coin.connect("coin2_collected", self, "_on_coin2_collected")
Also if your difference in coins only in amount of points given, you can use one callback for both types of coins using params.
coin.connect("coin_collected", self, "_on_coin_collected", [1]) # for 1 coin
...
coin.connect("coin_collected", self, "_on_coin_collected", [5]) # for 2 coin
Then your callback will look like
func _on_coin_collected(points):
score += points
Then first type of coins will give 1 point, and second type will give 5 points. But this is optimal solution if difference is only in amount of points given.
AlexTheRegent | 2021-01-07 14:31
No, no, no, Sorry if I’m troubling you a lot. But I think you misunderstood, it is that coin
has a separate score and coin2
has its own separate score. It’s not that coin
has +1
and coin2
has +5
score; both have +1
score in their own two distinct score counter variables, namely coinscore
and coin2score
.
Then use first code example
func coinspawner():
var rcoll = (collectibles[randi()%collectibles.size()])
var coin = rcoll.instance()
if rcoll == collectibles[0]:
coin.connect("coin_collected", self, "_on_coin_collected")
else:
print("carrot spawned!")
coin.connect("coin2_collected", self, "_on_coin2_collected")
AlexTheRegent | 2021-01-07 15:29
It says Invalid call. Nonexistent function 'instance' in base 'Node2D (collections.gd)'.
for var coin = rcoll.instance()
.
I think because rcoll
itself is an instance. Then What, Sir ?
How can I simply check if an instance is of a specific scene ?
Okay so thanks a lot Sir, I finally solved the problem with this little piece of code:
var rcoll = (collectibles[randi()%collectibles.size()]).instance()
#if rcoll == (collectibles[0]).instance(): #I tried these
#if rcoll is coins: #but none worked
if rcoll.get_filename() == coins.get_path():
rcoll.connect("coin_collected", self, "_on_coin_collected")
else:
rcoll.connect("carrot_collected", self, "_on_carrot_collected")
I hope this approach has no problems. plz reply.
and marked your answer as best 'cuz it solved the initial question I had asked.
Thanks again @AlexTheRegent
If your collectables
is array of load("*.tscn")
scenes, it can’t be Node2D
. When load
loads scene, it will be PackedScene
that have instance
method.
As for coin spawning function, I am still suggesting usage of int
variable as switcher because it is faster than checking types of scenes (your method is working, but it is not optimal).
var coin_type = randi()%collectibles.size()
var coin = collectibles[coin_type].instance()
if coin_type == 0:
coin.connect("coin_collected", self, "_on_coin_collected")
else:
coin.connect("carrot_collected", self, "_on_carrot_collected")
This code is simply cleaner and faster.
AlexTheRegent | 2021-01-07 23:33