So, I have just been growing in my understanding of return. I realize that much of what I was doing with signal (which made it confusing to track down where they were connected, and also added lines of code to connect all the signals) I can do by returning values. Is there any reason that I shouldn't go ham of using return when I need to pass a value back up the tree, or sometimes horizontally (having passed a reference from a mutual parent).

Messing around with my code I have seemed to be able to write everything way more streamlined using return. I just don't want to go ham and end up committing a coding faux pas.

  • Jesusemora replied to this.
  • xRegnarokx If you call a function that's named get_something(), the common sense is to expect it to return that something immediatelly via the return mechanism. Not to send some signal afterwards.

    If that something becomes available at some unknown future time, only then it makes sense to use a signal. And the way to tell someone to send you a signal is connect()

    If a child is trying to call/access something in an assumed parent, you're likely doing something wrong with your scene organization. You should rethink the architecture so that this call is not needed. If a child needs something from the parent, it's parent responsibility to assign it to the child.

    If you call a function in a node, it will return immediately, no matter how much code it has. All the code will be executed at once and the value will be returned immediately. Nothing else can happen while that function is running. The engine can only execute one function at a time. There is no chance that a node can be deleted while it executes a function call. The exception is functions turned into coroutines using await. But that's another story. As a beginner you shouldn't be using await at all. If you currently do use it, you should again rethink the architecture to work without it.

    xRegnarokx So, I have just been growing in my understanding of return. I realize that much of what I was doing with signal (which made it confusing to track down where they were connected, and also added lines of code to connect all the signals) I can do by returning values. Is there any reason that I shouldn't go ham of using return when I need to pass a value back up the tree, or sometimes horizontally (having passed a reference from a mutual parent).

    congratulations, you are becoming a programmer!

    xRegnarokx Messing around with my code I have seemed to be able to write everything way more streamlined using return. I just don't want to go ham and end up committing a coding faux pas.

    everything can have a purpose, testing is the best way to find out.
    but I still don't understand what it is you are trying to achieve. signals and "returns" are completely different things used for different situations.
    every single function has a return, it's just that most return void, so we don't assign them to a variable.
    a signal on the other hand is used to do something when a condition is met, and it's built into the engine to make our lives easier.

    you can do whatever works best for you.

      Jesusemora Okay thank you. An example is my map node stores the start position of player when they spawn in. At start of game I have two codes variants, one with signal, one with return.

      game_world.get_player_pos() calls map.player_pos() which sends signal player_pos to game_world.set_player_pos(pos)

      Other example:
      game_world.get_player_pos()
      player.position = map.player_pos() which returns Vector2

      someone once told me "call down, signal up". it's worked for me so far.

        packrat Yes I was doing that, but for me if the child is initiating the communication it makes sense to signal up/over/out. However, I am not sure if it is the parent who initiates if it is still better to signal or just use return value.

        I guess for me using return value seems fairly safe if you use same principles as call/signal.

        I guess for me return seems better if the parent is wanting information. Signal would seem to be preferable if the child wants to initiate contact or the parent wants to tell the child to initiate contact somewhere else.

        Return
        Parent.get_smth() -> child.give_smth() return smth

        Signal
        Child.contact() emit_signal("call") -> parent.listen()
        Or
        Parent.do_smth() -> child.contact() emit_signal -> other_node.listen()

        That is how I visualize use case, but I have just begun to look at return, and am wanting to know is my thinking flawed, or does this follow good coding practices. With my current understanding of things it seems as long as there isn't potential for the calling node to be deleted before a return is finished there shouldn't be an issue where the return can't send a value back, and since return doesn't care who it returns value to it should have similar decoupling as signals.

        I guess if my thinking is flawed I'd love to have my thoughts elucidated.

        • xyz replied to this.

          xRegnarokx If you call a function that's named get_something(), the common sense is to expect it to return that something immediatelly via the return mechanism. Not to send some signal afterwards.

          If that something becomes available at some unknown future time, only then it makes sense to use a signal. And the way to tell someone to send you a signal is connect()

          If a child is trying to call/access something in an assumed parent, you're likely doing something wrong with your scene organization. You should rethink the architecture so that this call is not needed. If a child needs something from the parent, it's parent responsibility to assign it to the child.

          If you call a function in a node, it will return immediately, no matter how much code it has. All the code will be executed at once and the value will be returned immediately. Nothing else can happen while that function is running. The engine can only execute one function at a time. There is no chance that a node can be deleted while it executes a function call. The exception is functions turned into coroutines using await. But that's another story. As a beginner you shouldn't be using await at all. If you currently do use it, you should again rethink the architecture to work without it.

            xyz Thank you for the response. I don't use await currently in my code.

            Sorry I should have been more accurate with my example, I was rushed. Yes I understand connect() is for connecting signal, and emit_signal is what sends the signal out.

            I think I poorly explained about signals, I don't have a child ever asking for something but rather I was trying to explain I saw signals as useful if the child is responding to something and needs to inform it's parent, it can send that signal. It isn't asking for information but rather giving it in response to something.

            However, if I understand your point that is at the heart of my question, return can be used if the information is immediately (or after running the func) available. While signal is best used if the information is undetermined until some future point.

            Example
            Parent.get_num() -> child.give_num() return 4

            Parent._ready() -> child.is_colliding.connect(when_colliding)
            Parent.when_colliding() -> do_smth
            Child.collision(collide:bool) -> if collide: -> emit_signal("is_colliding)

            Hopefully this is a bit clearer about my understanding.

            • xyz replied to this.

              xRegnarokx Yes. If the parent for example needs to get child's current hitpoint status, it'd call get_hitpoints() and receive it immediately by the returned value. On the other hand, if the parent needs to do something when a child gets hit, which may happen at some unknown time in the future, it should receive a corresponding signal from the child. A signal can pass additional information relevant to the event via signal arguments, for example how many hitpoints the child lost from that hit.

              Strictly speaking a signal emission is nothing more than a function call (from child to parent in this case, and in general from emitter to recipients). You just don't do this call explicitly. Instead you tell the engine to automatically make a call to predetermined signal handling functions. When you call connect() you just add another such function to signal's list of functions to call when it's emitted.

              The main advantage of using signals is that you can make many connections to a signal, so many such function calls just happen automatically. Besides that, you don't need to care if a signal recipient is there or not. If a recipient gets deleted, the signal will just disconnect and nothing bad will happen. The engine takes care of it. Whereas with explicit function calls it's your code's responsibility to make sure that the called object is there and has an expected type.

                xyz Thank you, that matches with what I've understood. I will take that knowledge and apply it my usage of return vs. Signals.