在Unity中,Unity如何神奇地调用所有“接口”?
Unity有一个“接口”,因为他们称之为IPointerDownHandler
( doco )。
你只需实现OnPointerDown …
public class Whoa:MonoBehaviour,IPointerDownHandler { public void OnPointerDown (PointerEventData data) { Debug.Log("whoa!"); } }
Unity将“神奇地”在任何此类MonoBehavior中调用OnPointerDown
。
您无需注册,设置事件或执行任何其他操作 。
您所做的只是将“IPointerDownHandler”和“public void OnPointerDown”添加到类中,您可以随时神奇地获取这些消息。
(如果你不是Unity开发者 – 如果你在游戏运行时突然在编辑器中添加一个,它甚至可以工作!)
他们到底怎么做?
我怎么能用扩展名做到这一点? 它更像是一个抽象类 – 他们是如何做到的? 所以,我想这样做:
public interface IGetNews { void SomeNews(string s); }
然后我可以将SomeNews
添加到任何MonoBehavior中 ,从而随时 “向这些项目发送新闻”。
我完全熟悉各种替代解决方案。 我想知道他们是如何实现这种“神奇”的行为。
(顺便说一句:我觉得Unity 不应该调用这些接口 ,因为它基本上没什么就像接口一样 – 它恰恰相反! 。你可以说它们神奇地想要从一个以上的抽象类inheritance,我想。)
在旁边:
为了清楚起见,如果你之前没有使用过Unity,那么传统的方法(即,因为我们无法访问Unity魔法)是这样的:只需将一个UnityEvent添加到你的守护进程中,它将发送消息题:
public class BlahDaemon:MonoBehaviour { public UnityEvent onBlah; ... onBlah.Invoke();
假设您有Aaa,Bbb,Ccc等课程,希望得到这些消息。 如果您是UnityEvent的新手,通常只需将Aaa,Bbb,Ccc中的“编辑器”拖到BlahDaemon的活动中即可。 但是你可以在代码中“注册”:
public class Aaa:MonoBehaviour { void Awake() { BlahDaemon b = Object.FindObjectOfType(); b.onBlah.AddListener(OnBlah); } public void OnBlah() { Debug.Log("almost as good as Unity's"); } }
这与使用Unity的内部魔法一样“差不多”,尽管在代码中更加混乱。 既然你正在“注册”以便在Awake
发言,那么你确实正在使用同样神奇的Unity。
对于XXXUpdate,OnCollisionXXX和其他MonoBehaviours,Unity注册的方式并不是reflection,因为它已被广泛认为是一些内部编译过程。
如何更新已被呼叫
不,Unity每次需要调用时都不会使用System.Reflection来查找魔术方法。
相反,第一次访问给定类型的MonoBehaviour时,底层脚本将通过脚本运行时(Mono或IL2CPP)进行检查,无论它是否定义了任何魔术方法,并且此信息都被缓存。 如果MonoBehaviour具有特定方法,则会将其添加到正确的列表中,例如,如果脚本定义了Update方法,则会将其添加到需要每帧更新的脚本列表中。
在游戏中,Unity只是遍历这些列表并从中执行方法 – 这很简单。 此外,这就是为什么Update方法是公共的还是私有的并不重要。
http://blogs.unity3d.com/2015/12/23/1k-update-calls/
在接口的情况下,我认为它需要更多,因为需要接口。 否则,您只需像任何其他MonoBehaviour方法一样添加方法。
我的假设(可能是错误的),它在这个GameObject上使用了一个基本的GetComponents。 然后迭代生成的数组并调用HAS TO BE实现的方法,因为它来自接口。
您可以使用以下内容重现该模式:
NewsData data; if(GetNews(out data)) { IGetNews [] getNews = data.gameObject.GetComponents(); foreach(IGetNews ign in getNews){ ign.SomeNews(); } }
GetNews是一种检查是否应该将某些新闻发送到对象的方法。 您可以将它想象为Physics.Raycast,它将值赋给RaycastHit。 如果该对象是出于任何正当理由接收新闻的话,它会填充数据引用。
您可以使用reflection来获取实现特定接口的程序集中的所有类型,然后实例化这些类型并通过接口调用这些实例上的方法。
var types = this.GetType().Assembly.GetTypes() .Where(t=>t.GetInterfaces().Contains(typeof(IGetNews))); foreach (var type in types) { var instance = (IGetNews) Activator.CreateInstance(type); instance.SomeNews("news"); }