Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Call method by name without knowing what component it's in?

Discussion in 'Scripting' started by zacharyhunterlund, Jul 5, 2023.

  1. zacharyhunterlund

    zacharyhunterlund

    Joined:
    Mar 7, 2021
    Posts:
    52
    Hello,

    In my game, I am programming a custom physics system, which uses a component called ControllableBody. This component is able to move and collide with both Tilemaps and other ControllableBodies via classic rectangle collision.

    I want to make it so that when two ControllableBodies collide, both will call a method called OnControllableBodyCollision(). This method is not located within the ControllableBody component. Just like regular physics methods (OnCollisionEnter, OnCollisionStay, etc.), it could be located in any script. I want to make it so it runs the method from any components that have it. Anyone know how to do this?
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,140
    What you might want is interfaces, then you would use GetComponent<InterfaceName>().MethodName.

    But, if you have several classes implementing the interface attached to the gameobject, then it will pick whatever the first one is. If you don't plan to, then that's still the way to go.

    Of course, you could get a collection of the scripts with the interfaces and loop through it if you planned to have multiple and want them all to run.
     
    Ryiah, lordofduct and Bunny83 like this.
  3. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    449
    You want events but your naming is a bit off. When two bodies collide, this should publish a "collision" event within an event handler. In your event handler, whomever wants to listen to those events, should subscribe with an "OnCollision" method, and the event handler will invoke an event in all subscribers upon an event.

    You can do this with C# Events and the EventHandler delegate https://www.tutorialsteacher.com/csharp/csharp-event

    There are also more sophisticated event handler libraries.
     
    Munchy2007 likes this.
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    I'm with @Brathnann's approach.

    While technically we're talking about an observer pattern, which C# events facilitate. It comes down to how the subject and observer are associated and it doesn't appear OP wants the registration of the observers with the subject to be done in a manner like C# events would require.

    They're specifically asking for something that behaves similar to OnCollisionEnter or OnCollisionStaty.

    And when I want something like that I use exactly what Brathnann suggested. I do it with interfaces. I usually have an extension method that allows me to bake the entire get all observers on a GameObject without having to boilerplate it everytime:
    https://github.com/lordofduct/space...pacepuppy.core/Runtime/src/Utils/Messaging.cs

    Used something like:
    Code (csharp):
    1. gameObject.Broadcast<IMessageObserver>(o => o.OnMessage());
    I have:
    Signal - all observers on the gameobject
    SignalUpwards - all observers on the gameobject and parents
    Broadcast - all observers on the gameobject and children

    My name choices might actually sound familiar if you're familiar with the GameObject's interface.

    SendMessage - https://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html
    SendMessageUpdwards - https://docs.unity3d.com/ScriptReference/GameObject.SendMessageUpwards.html
    BroadcastMessage - https://docs.unity3d.com/ScriptReference/GameObject.BroadcastMessage.html

    Which basically do the same thing but with a string named method.

    Which OP could do alternatively to get the behaviour they want:
    Code (csharp):
    1. gameObject.SendMessage("OnControllableBodyCollision");
    Though amongst many of us on the forums these "magic string" approaches are considered less favorable hence the desire for interface approaches.

    But if you really truly wanted something that behaved like how "OnCollisionStay" or "OnCollisionEnter" works... in that you don't even have to define an interface and implement. That's actually how you'd get it.
     
    Last edited: Jul 6, 2023
    Ryiah and KillDashNine like this.
  5. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    449
    I'm only suggesting events cos it's the best way to decouple stuff. I have for example a ParticleSystem pool that leases collision effect gameobjects, and this class is interested in collisions, so it's listening to them. I also have an AudioController that leases pooled AudioSources, and that also listens to collisions. So, when objects collide, that's an event, and whomever is interested in these events can join the party at a later stage.

    And this way the pattern is not Observer but Publish-Subscribe, my particle and audio controllers don't know who is colliding, they just want to see all collisions.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    The publish-subscribe pattern is just an observer pattern with an intermediary.

    And C# events aren't inherently the publish subscribe pattern. The intent of the publish subscribe pattern by introducing the intermediary is not requiring the publisher OR the subscriber to be aware of one another... that's the entire point of the intermediary.

    Lets give the simplest example of an event in use:
    Code (csharp):
    1. public class ButtonSubject
    2. {
    3.     public event System.EventHandler Clicked;
    4.  
    5.     void Update()
    6.     {
    7.         //test if mouse is over and pressed
    8.         if (mouseIsOverAndPressed()) Clicked?.Invoke(this, System.EventArgs.Empty);
    9.     }
    10.  
    11. }
    12.  
    13. public class ButtonActionObserver
    14. {
    15.  
    16.     public ButtonSubject Button;
    17.  
    18.     void Initialize()
    19.     {
    20.         Button = new ButtonSubject();
    21.         UIElements.Add(Button);
    22.         Button.Clicked += (s,e) => {
    23.             //do stuff
    24.         };
    25.     }
    26.  
    27. }
    This is a pretty basic example that you'd likely find on MSDN for say something like buttons in a WinForms situation. Or even a button here in Unity.

    In this example the observer is clearly aware of the subject they are deciding to observe.

    And the Button contains an indirect list of all of its observers in the form of the Clicked delegate (which underneath is just a list of all receivers. Depending the .net implementation in the form of an array or a linked list).

    This follows the basic layout of the observer pattern:


    Note that the 'observerCollection' in this, while often famliarized as a List or HashSet in many concrete examples, really just needs to come in the form of any way to store references to all observers and a way to notify those observers. The delegate facilitates this by being the collection in question.

    Relationship wise those there is an observer, and a subject, and the observer directly is aware of the subject.

    You could get even more tightly integrated where the button is replaced in a textfield and the observer specifically accesses the textfield's text property during the textfields 'onexit' event or something. This would impose a direct reliance on the observer's knowledge of the subject not just for registering for the event but also for acting on the event.

    ...

    Now lets give an example of a publisher subscriber pattern... this one I'm going to use a found article for:
    https://www.c-sharpcorner.com/Uploa...scriber-pattern-with-event-or-delegate-and-e/

    I want to note this article's first sentence:
    In this article it demonstrates created the publisher subscriber pattern using an event, an IPublisher, a concrete Publisher, a Subscriber, and a Message. The Subscriber has the publisher injected into its constructor in the form of the indirect IPublisher. It is not aware of WHO the publisher is... only that some publisher exists. The only thing the subscriber is concerned about is the Message it received an handling that message.

    Mind you this approach is not the only way to do it. It's just a pretty simple approach using interfaces. And of course events often have eventargs which can behave as the 'message' in this example. Which is why events effectively do assist in implementing a publisher subscriber pattern. But as I demonstrated above... it is not inherent to it and creating a more tightly couple example where the subscriber is well aware of its publisher can be done in events proving it is not inherent.

    ...

    With all of that said my point when I say we're "talking about an observer pattern". It'd be like I said "we're talking about fruit" where you said "no it's an orange". Yep... an orange is a fruit. And the publisher subscriber pattern is an observer pattern.

    Furthermore the message patterns I and Brathnann above described is also a publisher subscriber relationship. The message sender is unaware of who its sending messages to, and the subscribers are unaware of who its receiving messages from.
     
    Last edited: Jul 6, 2023
  7. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    449
    Sure, I'm not really concerned if Publish-Subscribe falls under which category name, this is just a name. Point is, in Observer pattern the observer is observing a single subject, such as a gameobject, as was in your example that uses Messaging.

    I'm only recommending Publish-Subscribe, as it is the basic form of real eventing, and is common to any real event systems you'll encounter. You are observing events, that are decoupled from both whoever is originating them and from whomever is listening to them. And this is different from messaging or signalling, that are about how to distribute your message.

    Anybody are free to disagree with the importance of decoupling or event based systems, of course, or go any way they prefer about these things. Just saying that when you go from computer games to real world software, you're likely to see event-based anywhere that's relatively recently built. But with games, whatever works for you is good to go.

    In my own game I use the Opsive Event System that comes with their character controller and uses a DLL under the hood. It uses GameObjects as event queues. I wouldn't recommend anybody buying it just for the event system, they just happened to have it and were using it so I decided to use it cos it has what I expect from such a system.
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    I find this interesting because you specifically said:
    Which has more to do with "how to distribute your message".

    That's all 'event' is... it's how to distribute a message.

    ...

    But yes, delegate/events are also a way to get the job done. And creating a layer of indirection through events is a possibility... so is it with the interface approach Brathnann described.

    But going back to OP's question. They specifically wanted something that behaved similarly to how OnCollisionEnter/OnCollisionExit works. There are millions of ways "real eventing" can be implemented... but if OP wants something similar to that. IMO, these are the one's the fit that bill. But you don't need to agree.
     
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    I had to look up this Opsive EventSystem...

    https://opsive.com/support/documentation/ultimate-character-controller/programming-concepts/events/

    It's literally SendMessage!

    They even say in that link the benefit of this over unity's is less garbage. Which yeah... that's technically true. But doesn't mean it's a completely different design pattern.

    Oh it also supports global dispatching too. Something I didn't touch on when I linked it, but my Messaging/interface system above also supports that.

    These are all literally the same exact design pattern!
     
  10. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    449
    Oh, no no. Events typically hold data and they can replace your whole database if you use them a lot (event sourcing). The event is the data that represents "something happened".

    But at least I'm not using the Opsive thing like this. Since gameobjects are the event queue, you can listen to specific objects, global objects or anything in between. A lot are player events, then there's like collisions that are with anything with colliders.... a lot of my events go to decoupling UI from the game.