在Unity中,Unity如何神奇地称呼所有"接口"?

Fat*_*tie 29 c# interface unity-game-engine

Unity有一个"界面":

IPointerDownHandler(doco)

你只需实现OnPointerDown ...

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

Unity将"神奇地"调用OnPointerDown任何此类MonoBehavior.

您不必注册,设置事件,也不需要做任何其他事情.

你在语法上做的就是在类中添加"IPointerDownHandler"和"public void OnPointerDown",你可以神奇地获取这些消息.

(如果你不是Unity开发者 - 如果你在游戏运行时突然在编辑器中添加一个,它甚至可以工作!)

他们到底怎么做,我该怎么做?

所以,我想这样做:

public interface IGetNews
 {
 void SomeNews(string s);
 }
Run Code Online (Sandbox Code Playgroud)

然后我可以添加SomeNews 到任何MonoBehavior.

替代解决方案是显而易见的,我想具体了解Unity如何实现"魔术"行为.

(顺便说一句:我觉得他们不应该称这些"界面",因为它基本上没什么就像一个界面 - 它恰恰相反!你可以说他们神奇地想要从一个以上的抽象类继承,我猜测.)


在旁边:

如果您之前没有使用过Unity,那么传统的方法 - 因为我们无法访问Unity魔法 - 只需向您的守护程序添加一个UnityEvent,它将发送有问题的消息:

public class BlahDaemon:MonoBehaviour
  {
  public UnityEvent onBlah;

    ...
    onBlah.Invoke();
Run Code Online (Sandbox Code Playgroud)

假设您有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");
    }
  }
Run Code Online (Sandbox Code Playgroud)

你基本上是"注册"你的电话Awake,你确实在捎带神奇的Unity使用 - 无论它是什么.但我想直接使用魔法.

Eve*_*rts 26

对于XXXUpdate,OnCollisionXXX和其他MonoBehaviours,Unity注册的方式不是反射,因为它被广泛认为是一些内部编译过程.

如何更新已被呼叫

不,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<IGetNews>();
    foreach(IGetNews ign in getNews){ ign.SomeNews(); }
}
Run Code Online (Sandbox Code Playgroud)

GetNews是一种检查是否应该将某些新闻发送到对象的方法.您可以将它想象为Physics.Raycast,它将值赋给RaycastHit.如果该对象是出于任何正当理由接收新闻的话,它会填充数据引用.


Mar*_*ade 5

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

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");
}
Run Code Online (Sandbox Code Playgroud)


小智 1

依赖于 UI 的内置接口(如 IPointerDownHandler、IDragHandler 等)由EventsSystem类/脚本调用[这附加在创建 UI/Canvas 对象时自动创建的 EventSystem GameObject 上],并且仅适用于 UI 元素 [用于测试如果您从场景中关闭或删除 EventSystem GameObject,甚至禁用EventsSystem脚本,这些接口将不会被调用,所有 UI 元素将停止工作(从功能角度来看,意味着您的注册函数将不会被调用)]。

因此,这些接口方法本身并没有神奇地被调用。这些是通过EventsSystem脚本调用的。

了解事件系统: 点击这里

为了与 Unity 中的 UI 元素进行交互,您需要记住 3 个主要组件:

  1. GraphicRaycaster:它附加到 Canvas 对象本身。它负责将光线投射发送到该画布的 UI 元素,并确定其中是否有任何元素被击中。如果将其从画布中删除,则不会与该画布的 UI 元素发生交互,例如单击、滚动等,并且这些接口也不会调用。[LINK FOR MORE][2]

  2. InputSystemUIInputModule::它附加在 EventSystem Gameobject 上,负责告诉整个 Unity 场景中的画布,将什么视为 UI 的输入,反之亦然。比如将鼠标左键点击UI视为UI元素的输入等,与调用OnPointDown、OnDragStarted等接口相关的方法链接。了解更多:链接 在此输入图像描述

  3. EventSystem:负责处理和处理整个Unity场景中的UI事件。它不能独立工作,需要BaseInputModules正常工作,并且还维护元素的状态或用户交互。详情请见:链接

为了便于理解,请将其视为一个故事:EventSystem 使用 InputSystemUIInputModule 从鼠标、键盘或触摸获取输入,并根据这些输入,EventSystem 调用 RayCast 来判断您是否与任何元素进行了交互(保存该元素的引用)如果是,则通过 InputSystemUIInputModule 调用内置函数,例如悬停、选择、鼠标向下/向上、根据生命周期在该元素上取消拖动(鼠标/触摸指向的元素存储在 EventSystem.current 中) 。

现在,如果您想调用任何 IPointerDownHander 方法,也许它们在单击 UI 元素时会在内部执行此操作,反之亦然: IPointerDownHander pointerDownHander = EventSystem.Current.GetComponent<IPointerDownHander>();//假设将接口转换为对象(如果该接口附加到)对象,然后该对象将被返回,您将能够调用接口注册的方法。

if(ipd) ipd.OnPointerDown(var etc)
Run Code Online (Sandbox Code Playgroud)

或下面的代码复制自Unity UI Package,您可以在其中更准确地了解此执行情况

// Invoke OnPointerDown, if present.
                var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.pointerDownHandler);
if (newPressed == null)
    newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); //copied from the Unity UI package
Run Code Online (Sandbox Code Playgroud)