Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Unity, how does Unity magically call all "Interfaces"?

Unity has an "interface":

IPointerDownHandler (doco)

You simply implement OnPointerDown ...

public class Whoa:MonoBehaviour,IPointerDownHandler     {     public void OnPointerDown (PointerEventData data)         { Debug.Log("whoa!"); }     } 

and Unity will "magically" call the OnPointerDown in any such MonoBehavior.

You do NOT have to register them, set an event, nor do anything else.

All you do syntactically is add "IPointerDownHandler" and "public void OnPointerDown" to a class, and you can get those messages magically.

(If you're not a Unity dev - it even works if you suddenly add one in the Editor while the game is running!)

How the hell do they do that, and how can I do it?

So, I want to do this:

public interface IGetNews  {  void SomeNews(string s);  } 

and then I can add SomeNews to any MonoBehavior.

The alternate solutions are obvious, I want to know specifically how Unity achieve that "magic" behavior.

(BTW: I feel they should not have called these "interfaces", since, it's basically nothing at all like an interface - it's sort of the opposite! You could say they magically made a way to inherit from more than one abstract class, I guess.)


Aside:

if you've not used Unity before, the conventional way to do this - since we don't have access to Unity magic - is just add a UnityEvent to your daemon which will be sending the message in question:

public class BlahDaemon:MonoBehaviour   {   public UnityEvent onBlah;        ...     onBlah.Invoke(); 

Say you have classes Aaa, Bbb, Ccc which want to get the message. Simply connect the Unity event (either by dragging in the editor or in code), example:

public class Aaa:MonoBehaviour   {   void Awake()     {     BlahDaemon b = Object.FindObjectOfType<BlahDaemon>();     b.onBlah.AddListener(OnBlah);     }      public void OnBlah()     {     Debug.Log("almost as good as Unity's");     }   } 

You're basically "registering" your call in Awake, you are indeed piggybacking on the magic Unity use - whatever it is. But I want to use the magic directly.

like image 635
Fattie Avatar asked Mar 27 '16 18:03

Fattie


People also ask

Are interfaces serializable in unity?

Since Unity 2019.3 interface-type fields are serializable.

How do you implement an interface in unity?

Implementing an Interface During the class declaration, after the class name, add a colon ':' followed by the interface name. The class must then implement every member declared in the interface, making them all public.


1 Answers

When it comes to XXXUpdate, OnCollisionXXX and other MonoBehaviours, the way Unity registers is not reflection as it has been widely believed but some internal compilation process.

HOW UPDATE IS CALLED

No, Unity doesn’t use System.Reflection to find a magic method every time it needs to call one.

Instead, the first time a MonoBehaviour of a given type is accessed the underlying script is inspected through scripting runtime (either Mono or IL2CPP) whether it has any magic methods defined and this information is cached. If a MonoBehaviour has a specific method it is added to a proper list, for example if a script has Update method defined it is added to a list of scripts which need to be updated every frame.

During the game Unity just iterates through these lists and executes methods from it — that simple. Also, this is why it doesn’t matter if your Update method is public or private.

http://blogs.unity3d.com/2015/12/23/1k-update-calls/

In the case of an interface, I would assume it does a bit more since the interface is required. Else, you would just add the method like any other MonoBehaviour methods.

My assumption (that could be wrong), it uses a basic GetComponents on this GameObject. Then iterate the resulting array and call the method that HAS TO BE implemented since it is from the interface.

You could reproduce the pattern with:

NewsData data; if(GetNews(out data)) {     IGetNews [] getNews = data.gameObject.GetComponents<IGetNews>();     foreach(IGetNews ign in getNews){ ign.SomeNews(); } } 

GetNews is a method that checks if some news should be sent to the object. You could think of it like Physics.Raycast that assigns values to a RaycastHit. Here it fills a data reference if that object is meant to receive news for any valid reasons.

like image 86
Everts Avatar answered Sep 18 '22 00:09

Everts