#I think I'm using signals incorrectly

104 messages · Page 1 of 1 (latest)

eager geyser
#

I'm working on a game that resembles something along the lines of brotato / vampire survivors (original, i know)

And currently i have in my head that signals are a bit like events.
however, they're isolated events.

What i want, is to have a item, that listens for a "event" with a specific name, say "item_picked_up", or "gold_collected" or "enemy_killed", you get the idea.
And to accomplish this, i had in mind to add a text input for a item's effect, with the event name in it.
so when that event is called, the item knows it needs to apply its effects, and whabam it's done.

However, i've come to realise, that it's not that easy, and probably i'm doing it wrong.
Since i need the node to connect the signal from, however, it being a text input, that's kind of impossible to do-
Sure, i can have a enum with signals in it, but that feels like a hack more than the way i'm supposed to do it

thanks in advance

harsh zodiac
#

you could do a single "event"-signal that has a string/integer/enum as one of its properties to represent the speciffic event.

signal event(index)

then do event.emit("item_picked_up") for example.
and the nodes that connect to the signal can filter which event is relevant to them.

#

and as far as i know an enum only ever represents numbers or strings, you can't put something like a signal in it.

hazy stump
#

However, I am still not 100% sure what his question is. I'll try to see if I understand it first.

harsh zodiac
hazy stump
#

Wait nvm

#

Even dictionary can hold references

#

My bad

#

Alright I'll first have a thorough look at his/her question.

#

I think I understand the question now.

#

Now how to do it🤔

#

Since signals are constant and can't be increased or decreased at run time. The total number signals are always known.

#

And as to how to distinguish between the ones......

#

I would say using enum would be good but I thinks bool would be fine.

#

But enum would be more readable.

#

Hmmm

hazy stump
#

Depending on the structure either this method can be useful or not.

#

I would say using an export of all the signals then in editor select from the array of enum which signal you want to connect to. In _ready checking the enums and comparing to connect the ones and then connecting it.

#

However that itself seems half assed.

harsh zodiac
#

OP discribed that the system is supposed to listen to certain events and trigger effects if it's the correct one.
the closest godot has to that structure is probably the _notification(what : int)
which is pretty much exactly what i tried to describe.

hazy stump
harsh zodiac
hazy stump
harsh zodiac
#

would get rid of the problem of signals being limited

hazy stump
#

I'd personally have used resources if I had faced this problem. But that could be hard to explain to a beginner.

#

I am assuming they're a beginner since they've doubts with signals.

#

I'd have created a resource each one responsible for connecting a different signal. Then in the item itself. I'd have exported an array of that resource's class which gives me option to create instances of any of such resources. And on run time I can iterate over those resources and ask them to connect this signal.

harsh zodiac
#

hm, do you mean something like this?

class_name event
extends Resource

@export var name : String 

signal triggered()
hazy stump
#

But now even that seems hactic😅

eager geyser
#

or just that one signal globally?

hazy stump
harsh zodiac
#

they said you'd have to iterate over all that connect. so it would be something like a DIY signal.

harsh zodiac
#

i mean that instead of using the built in logic of calling all callables that are listening to a signal, just Do It Yourself.

#

but i don't think that would be the best idea

hazy stump
#

Well, at this point. I would say connect all the signals to one function each. And only define the functions that you need to do something. Other will just have pass and won't do anything

#

Well I don't even know if all these signals will be connected to a single function or different function?

eager geyser
#

i have the node setup for the player like this:

o Player
- o Hurtbox (Area2d)
- - o HurtShape (CollisionShape2D)
- o Pickup (Area2d)
- - o PickupShape (Collisionshape2D)
- o HealthComponent (Node)
- o PlayerStats (Node)

where

  • hurtbox emits "event" damage_taken
  • pickup emits "event" item_picked_up
  • HealthComponent emits "event" health_healed
  • PlayerStats emits "event" stats_changed

or something along the lines of that

hazy stump
#

If I have to give you an idea as to how signals work, think that they have an array of callables.
Everytime you connect a callable to a signal. It stores its reference in that array and when you emit the signal. It iterates over that array and call all of those callables.

eager geyser
#

is the array local to the node the signal emits from, or a global array for every node that emits said signal

hazy stump
#

I think using an enum would be better than anything I've recommended till now.

harsh zodiac
#

how exactly would we use the enum here?

hazy stump
hazy stump
#

To be honest. I might have created separate items depending on what signal I am listening to.

#

Using inheritance

harsh zodiac
hazy stump
harsh zodiac
#

yes, but it can get annoying when creating the game

eager geyser
#

i use enums for the player stats, and the arrays are created based on the enums so they're always 1:1

harsh zodiac
#

well you two seem to understand eachother better.
I would personally use a custom resource to hold the signal. that way i could easily create a resource for each event, and just add it both at the places that wait for the event and the ones that would trigger it.
But to each their own.

hazy stump
harsh zodiac
#

thats fair

hazy stump
#

The old way is node.connect("signal_name", listener)

#

In godot 3 you also had to pass self as a parameter

harsh zodiac
#

since callables didn't exist yet

hazy stump
#

another way people use is
node.connect("signal_name", Callable(self, "listener"))

harsh zodiac
#

no it defines the node that has the funciton that gets connected

hazy stump
#

self is no longer needed

harsh zodiac
#

yes, but you said it was needed to get the node that has the signal, and that's just incorrect.

hazy stump
harsh zodiac
#

you called the function connect on the node that has the signal, then pass the object that has the function you want to connect.

#

connect ( String signal, Object target, String method, Array binds=[ ], int flags=0 )

hazy stump
harsh zodiac
#

you can only call connect on the object that has the signal

hazy stump
harsh zodiac
#

that's from the docs from godot 3.5

hazy stump
hazy stump
#

Ah its 2nd

#

Seems I was wrong

#

Sorry

harsh zodiac
#

np, i think we shouldn't argue about something like that here anyway.

hazy stump
harsh zodiac
#

we're here to help

hazy stump
harsh zodiac
#

me neither, but i couldn't think of a better word

hazy stump
harsh zodiac
#

no idea why i didn't think of that 😅

hazy stump
#

🐐

harsh zodiac
#

and i meant that the connect function doesn't contain which object has the signal, instead you would call object_with_signal.connect("signal_name", object_with_callable, "callable_name")

hazy stump
#

Also I never saw you in the beginner section. Do you only help in the help discussion?

harsh zodiac
hazy stump
harsh zodiac
hazy stump
#

I see

#

Well I just tried it yesterday.

#

I am gonna head out now. It was nice chatting with you, see ya.

harsh zodiac
eager geyser
#

since i too thought about that idea, but wouldn't you still need to connect it to the local node
or would it, since it's a resource, keep that or something?

harsh zodiac
# eager geyser since i too thought about that idea, but wouldn't you still need to connect it t...

the resource would contain the signal, you would still have to connect whatever function you want listening to that signal.
but since the custom resource is a class it would be type safe, so you don't have to worry if the signal exists or not.
the custom resource would look something like this:

class_name Event
extends Resource

# optional
@export var name : StringName

signal triggered()

you would then create a new resource from that class as a file.
then in whatever listens to that specific event give it an export property of type Event and drag the file into it with the Inspector.
then to connect it you would just have to do:

@export var event : Event

func _ready() -> void:
  if is_instance_valid(event):
    event.trigger.connect(my_callable)

then to emit the trigger-signal you would do something similar, but instead call event.trigger.emit().
but that would probably be something you hardcode so it won't use @export var and instead preload the Resource-File as a const.
As long as you don't tick local to scene on the resource in the Inspector it will always be the same one for each instance of your scene, so the signals will also be the same.

eager geyser
#

i think i got it working with that

#

thanks!