• [deleted]

I have the following proof-of-concept:

signal-autoload-test.zip
15kB

It has a counter label at the top, and a board (grid) below. The grid is populated with cells on _ready() (as there's too many to bother adding them manually via the UI). Each cell is also a grid (on a white background) and, on _ready(), is similarly populated with tiles (blue squares - also too many to bother adding manually). Each tile responds to being clicked on, where it changes colour and emits a signal so the counter can update (counting the number of tiles that have been clicked).

I initially struggled to work out how to get a node in the main scene (the counter) to connect to signals coming from each tile, which aren't in the main scene. And no node in the main scene 'knows' about them either - since the main scene only has the Board node, which is programatically populated with Cell nodes, which are then programatically populated with Tile nodes. Eventually I found this video which showed how to trigger signals via an autoload script. I did that, and it works. But I wonder if that's the best solution here...

So my question is, is triggering a signal in the autoload script the best way to solve this problem? Or is there a more elegant solution here?

  • xyz replied to this.
  • [deleted]
    Typically the script that instantiates a component should also make the connection.
    Since you have more than one nested level of components, you can basically approach it in two ways:
    1) let tiles signal up to cell, and then cell passes the signal up to whoever needs it
    or
    2) make a connect_each_tile_updated(callback) method in cell.gd that will connect all of its tile's signals to a callable passed as an argument, and call this from game.gd
    or alternatively
    2b) make get_all_tiles() method in cell.gd that returns array of tiles, and connect them all in game.gd.

    [deleted] I'd avoid introducing global signals (or global objects in general) unless it's absolutely necessary. In this case - it isn't. You can achieve the same functionality with the same amount of code without any globals, and most importantly without breaking the component encapsulation principle.

    Note that in the tutorial the author suggests this approach as to eliminate the need for instanced components to "know" the environment they've been instanced in. However, this is actually not happening as components still need to "know" about the singleton node, which is part of the "upper" environment. So this does not eliminate the need of a signal sender to presuppose what's "upstairs". But not only that. It also forces all of the receivers to behave that way, forcing the code to break the encapsulation principle for all of them.

    So I'd vote for a signal inside each tile, that's connected by the script when the tile is instantiated.

    Using a global signal technically works fine, but cultivating this approach may spaghettify your code flow in larger projects.

      • [deleted]

      Thanks xyz, good to know there's a better way!

      xyz So I'd vote for a signal inside each tile, that's connected by the script when the tile is instantiated.

      I initially wanted to try this, but couldn't work out the connection part... So tile.gd would have the signal definition and the emitting code, and then the connection code would go into cell.gd where tiles are instantiated? But, AFAIK, Cells don't know about the Counter, so how would they perform the connection?

      Can you please advise how this'd be done in this scenario?

      • xyz replied to this.

        [deleted]
        Typically the script that instantiates a component should also make the connection.
        Since you have more than one nested level of components, you can basically approach it in two ways:
        1) let tiles signal up to cell, and then cell passes the signal up to whoever needs it
        or
        2) make a connect_each_tile_updated(callback) method in cell.gd that will connect all of its tile's signals to a callable passed as an argument, and call this from game.gd
        or alternatively
        2b) make get_all_tiles() method in cell.gd that returns array of tiles, and connect them all in game.gd.