-
-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add static signals in GDScript #6851
Comments
Only instances can have signals, not classes. But a GDScript class is an instance of the Also note that if the class is unloaded (if the script has an |
Another workaround is to use the event bus pattern: define these signals in a dedicated autoload script for all static signals. # EventBus.gd
signal selected_cell_changed(cell: Cell)
# selected_cell.gd
class_name SelectedCell
static var selected_cell: Cell = null:
set = set_selected_cell
static func set_selected_cell(n_selected_cell: Cell) -> void:
if is_same(selected_cell, n_selected_cell):
return
selected_cell = n_selected_cell
EventBus.selected_cell_changed.emit(n_selected_cell)
# other_script.gd
func _ready() -> void:
# We don't need an object instance to use it.
EventBus.selected_cell_changed.connect(_on_selected_cell_changed)
func _on_selected_cell_changed(cell: Cell) -> void:
print(cell) The downside is you have to name the signals carefully. |
It would be interesting if all signals could double as static signals. Then you could choose to either connect to a particular instance's signals as usual, or instead connect to the whole class' signal to receive it when any instance emits it. For example |
Allowing to connect signals to autoloads (sigletons) (See #1694 and #4993) for the good it would do※ has a hurdle: When a scene is instantiated it is not in the scene tree, so it cannot reach autoload (singletons) at that moment. Which means such connection would have to be delayed. Perhaps this is desirable? An alternative is to have static signals, which is what is being proposed here. Since static signals would not depend on the scene tree, then connecting automatically to them when a scene is instantiated would be viable. That would, of course, require different proposals to the linked ones to allow connected them visually from the editor, which is what I'm interested in. I am interested in this in particular as a way to ease addons communicate with each other, without adding dependencies between them, and while the designer stays in control. ※: These are some of the situations where we would rather connect visually to a signal bus (event bus) instead of doing it from a script:
|
How would this even work? The only why you can access static members is with a class name or a ugly preload |
Instead of signals, you can now (since 4.1 I believe) have a static array which saves the listening objects. e.g. class_name Settings
static var listeners: Array
static func on_change_sub(object: Object): # To subscribe, usage: Settings.on_change(self)
listeners.append(object)
static func on_change_unsub(object: Object): # To unsubscribe, usage: Settings.on_change_unsub(self)
listeners.remove_at(save_listeners.find(object))
static func invoke():
for listener in listeners:
listener.onChange() Dont forget to unsubscribe nodes that get freed, or else it will try to call the method on a null instance. |
You can functionally utilize static signals (at least how I want to use them) now using a static var singleton pattern (for the instance), though it does feel hacky. Might be side effects to doing it this way? # settings.gd
class_name Settings extends RefCounted
static var singleton := Settings.new()
signal _changed
static var changed:Signal:
get: return singleton._changed
static func change( key:StringName, value ) -> void:
singleton.set( key, value )
Settings.changed.emit( key, value )
static var example_property := "foo" # test.gd
func _ready() -> void:
print( Settings.example_property )
Settings.changed.connect( func( key:String, value ):
print( "%s changed to %s" % [key, value] ))
Settings.change( "example_property", "bar" )
print( Settings.example_property )
Static variables aren't currently auto-completed though so |
I want to point out somebody figured out how to bind signals to a class and consume them using the expected syntax: https://stackoverflow.com/a/77026952/402022 What they are doing is adding - during initialization - a signal to the script using The static signal is then consumed by accessing the That works today, and I have used it successfully. I hope this clears any doubt of how static signals might work. |
I found this to be an easy workaround... extends Node3D
class_name Drone
signal _real_signal
static var singleton := Drone.new()
static var static_signal:= Signal(singleton._real_signal) Example use below func _ready() -> void:
connect_signal()
emit()
func connect_signal():
static_signal.connect(Callable(self, "getting_signal"))
func getting_signal():
print("we got the signal")
func emit():
print("emiting signal")
Drone.static_signal.emit() |
@TheJehoiada PS: Code blocks should use triple backticks like this (with an optional language name for syntax highlighting): ```gdscript I edited your post accordingly, but remember to do this in the future 🙂 |
Just wanted to post that I have found a more concise workaround for static signals, based on some of the other solutions that have been posted before. I created a new class that exists as a quick creator for static signals, and then you can refer to this function from anywhere: extends Object
class_name StaticSignal
static var static_signal_id: int = 0
static func make() -> Signal:
var signal_name: String = "StaticSignal-%s" % static_signal_id
var owner_class := (StaticSignal as Object)
owner_class.add_user_signal(signal_name)
static_signal_id += 1
return Signal(owner_class, signal_name) Here is an example of creating a static signal from this method: static var example_signal: Signal = StaticSignal.make() From here, you can use example_signal as you would expect with no other drawbacks that I know of. |
Describe the project you are working on
Strategy game.
Describe the problem or limitation you are having in your project
I have a singleton with many signals for different things. Sometimes a singleton can contain signals that should be moved elsewhere. After adding static variables, it would be nice to add static signals as well. This will create helper objects that do not need to be instantiated. These "objects" can be used as singletons.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
We just connect/disconnect anywhere. We don't need to have an instance of the class.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
An example of a class with a static signal:
Sample code that uses a static signal:
If this enhancement will not be used often, can it be worked around with a few lines of script?
Yes, it is potentially possible to use a static variable with a signal:
But then it still requires an "entry point" to assign that signal:
Is there a reason why this should be core and not an add-on in the asset library?
It's part of GDScript.
The text was updated successfully, but these errors were encountered: