提问者:小点点

在Unity中,Unity如何神奇地调用所有“接口”?


Unity有一个“接口”:

IPointerDownHandler(doco)

您只需实现OnPointerDown...

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

而Unity将在任何这样的行为中“神奇地”调用OnPointerDown

您不必注册它们,设置事件,也不必做任何其他事情。

从语法上讲,您只需将“IPInterDownHandler”和“public void on PointerDown”添加到类中,就可以神奇地获得这些消息。

(如果你不是Unity开发人员-甚至在游戏运行时突然在编辑器中添加一个,它也会起作用!)

所以,我想这样做:

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

然后我可以向任何MonoBehavior添加SomeNews

(顺便说一句:我觉得他们不应该称这些“接口”,因为它根本不像接口——它有点相反!我想,你可以说它们神奇地从多个抽象类继承。)

旁白:

如果你以前没有使用过Unity,那么传统的方法——因为我们没有使用Unity魔法——就是在你的守护进程中添加一个Unityevent,它将发送有问题的消息:

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

假设您有Aaa、Bbb、Ccc类想要获取消息。只需连接Unity事件(通过在编辑器中拖动或在代码中拖动),例如:

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");
    }
  }

你基本上是在“注册”你的呼叫,你确实是在利用神奇的统一使用——不管它是什么。但是我想直接使用魔法。


共2个答案

匿名用户

当涉及到XXXUpdate、OnCollisionXXX和其他单行为时,Unity注册的方式并不像人们普遍认为的那样是反射,而是一些内部编译过程。

不,Unity不使用System.反思找到一个神奇的方法,每次它都需要调用一个。

相反,第一次访问给定类型的MonoBehavior时,将通过脚本运行时(Mono或IL2CPP)检查底层脚本是否定义了任何神奇的方法,并缓存该信息。如果MonoBehavior具有特定的方法,则会将其添加到适当的列表中,例如,如果脚本定义了更新方法,则会将其添加到每个帧都需要更新的脚本列表中。

在游戏过程中,Unity只是在这些列表中迭代并执行其中的方法——就这么简单。另外,这就是为什么更新方法是公共的还是私有的并不重要。

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

在接口的情况下,我假设它做得更多,因为接口是必需的。否则,您只需像添加任何其他MonoBehavior方法一样添加该方法。

我的假设(这可能是错误的),它在这个游戏对象上使用了一个基本的GetComponents。然后迭代生成的数组,并调用必须实现的方法,因为它来自接口。

您可以使用以下方法复制该模式:

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

GetNews是一个检查是否应该向对象发送某些新闻的方法。你可以把它想象成物理。为光线投射命中指定值的光线投射。在这里,如果该对象出于任何正当理由要接收新闻,它将填充一个数据引用。

匿名用户

您可以使用反射获取实现特定接口的程序集中的所有类型,然后实例化这些类型,并通过接口调用这些实例上的方法。

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");
}