godot

How to share information from an inherited button scene to a parent scene through signals?


I am trying to create a DOMINO game, and to do this I've been created a main/principal scene and another scene using TextureButton as root. After that, I created many inherited Scenes using the TextureButton to represents stones in the DOMINO game.

It works well, but now I need to get values from the inherited scenes when the TextureButton is on pressed. I've already created the signal _on_pressed(), and I'm able to see the values inside of the TextureButton scene, but I don't know how could I get this value in the principal scene. I've tried to create a Global Group, but I don't know how can I get the values.

How could I access or read the TextureButton's values in the principal scene's script?

Below are the scripts for both the principal scene and TextureButton scene.

    class_name PrincipalScene extends Node2D
    
    ##CONFS
    @export_category("Pedras Domino")
    @export var _pedras = [
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn"),
        preload("res://DominoX/pedras/0_0.tscn"),
        preload("res://DominoX/pedras/0_1.tscn")
    ]
    var _rng = RandomNumberGenerator.new()
    
    @export_category("PANELS")
    #@export var _panel_topo: GridContainer
    #@export var _panel_bottom: GridContainer
    #@export var _panel_left: VBoxContainer
    #@export var _panel_right: VBoxContainer
    
    # Called when the node enters the scene tree for the first time.
    func _ready() -> void:
        _rng.randomize()
        _spawn_random_object()  
        
    
    func _process(_delta: float) -> void:
        pass
        
    func _spawn_random_object():
        #SELECT THE STONES 
        while(_controle.size() < _pedras.size()):
            var _random_index = _rng.randi() % _pedras.size()
            if(!self._controle.has(_random_index)):
                self._controle.append(_random_index)
        
        #GET THE STONES 7 EACH 7
        var _topo = _controle.slice(0,7)
        var _bottom = _controle.slice(7, 14)
        var _left = _controle.slice(14, 21)
        var _right = _controle.slice(21, 28)    
        
        #INSTANCE STONES 
        for i in _topo.size():
            $PanelTop/Top.add_child(self._pedras[i].instantiate())
            
        for i in _bottom.size():
            $PanelBottom/Bottom.add_child(self._pedras[i].instantiate())
            
        for i in _left.size():
            $PanelLeft/Left.add_child(self._pedras[i].instantiate())
            
        for i in _right.size():
            $PanelRight/Right.add_child(self._pedras[i].instantiate())


    class_name Stone extends TextureButton
    
    @export_category("Confs")
    @export var _valor: int 
    @export var _index: Array[int]
    var _click_stone: bool = false
    
    #STONE VALUE RETURNS 
    func get_valor_pedra() -> int:
        return _valor
        
    #STONE INDEX VALUE RETURNS 
    func get_index_pedra() -> Array[int]:
        return _index
    
    #STONE WAS CLICK    
    func get_click_stone() -> bool:
        return _click_stone
    
    func _on_pressed() -> void:
        print("Stone Click Value: " + str(get_valor_pedra()))
        print("Stone Click Array Values" + str(get_index_pedra()))
        self._click_stone = true
        self.get_valor_pedra()
        self.get_index_pedra()
        self.get_click_stone()
        print("Click Stone: " + str(self._click_stone))
    
    func _on_button_up() -> void:
        self._click_stone = false
        print("Click Stone: " + str(self._click_stone))
        

Solution

  • It seems like you are trying to have a scene broadcast information to its parent. A handy phrase to remember is "Call down & signal up." The gist being that it is best practice for a node to use something like get_node(...).dostuff(...) to interact with its children, and to use signals to interact with its parents.

    I found this article helpful when learning this.

    So, a solution to your problem is to create a signal upon a texturebutton press that passes a number, and connect the signal to the principal scene. Luckily, buttons already have a signal that occurs on pressing the texturebutton, so you just have to modify the signal to also pass a value. Essentially, the easiest way to do this is to create a new signal that gets triggered by, and replaces, the old signal.

    First, connect the pressed() signal from the texturebutton to itself. [click on texturebutton scene, then go to Node tab -> Signals tab -> right click pressed() -> connect -> choose texturebutton] This will create a _on_pressed() function. Create a new signal, domino_pressed(n), and have it passed in the body, as shown below.

    # in button's script
    ...
    
    signal domino_pressed(n)
    func _on_pressed() -> void:
        domino_pressed.emit(_valor)
    
    ...
    

    Second, just connect the new signal to your principal node. [click on texturebutton scene, then go to Node tab -> Signals tab -> right click domino_pressed() -> connect -> choose principalscene] This will create a _on_domino_pressed(n) function. Now, do whatever you want with the value, as it has been safely passed to the principal scene.

    # in principal scene script
    ...
    
    func _on_button_pressed_with_val(n) -> void:
        print("The value of that domino was %s !" %n)
    
    ...
    

    Signals are powerful and widely useful, once you learn when to implement them into your projects. Don't forget that the parameters passed with a signal can be anything, including info on the node itself- so you can identify and remember what nodes shared which signals when.