复制Unity的神秘接口能力

时间:2016-11-13 17:05:04

标签: unity3d

Unity3D有这样的界面,对于MonoBehavior上的任何组件,你只需这样做:

public class LaraCroft:MonoBehaviour,IPointerDownHandler
  {
  public void OnPointerDown(PointerEventData data)
        {
        Debug.Log("With no other effort, this function is called
        for you, by the Unity engine, every time someone touches
        the glass of your iPhone or Android.");
        }

必须注册,设置代理或其他任何内容。每当有人触摸屏幕时,OnPointerDown(IPointerDownHandler中唯一的项)就会被调用。

惊人!

这是我写的类似界面......

public interface ISingleFingerDownHandler
    {
    void OnSingleFingerDown();
    }

现在,我希望消费者能够做到这一点......

public class LaraCroft:MonoBehaviour,ISingleFingerDownHandler
    {
    public void OnSingleFingerDown(PointerEventData data)
        {
        Debug.Log("this will get called every time
        the screen is touched...");
        }

回顾一下,使用Unity的界面,该功能会在无需进一步努力的情况下自动调用 - 消费者必须注册或其他任何内容。

可悲的是,我只能这样做:

我写了一个“守护进程”..

public class ISingleFingerDaemon:MonoBehaviour
    {
    private ISingleFingerDownHandler needsUs = null;
    // of course that would be a List,
    // just one shown for simplicity in this example code

    void Awake()
        {
        needsUs = GetComponent(typeof(ISingleFingerDownHandler))
                                       as ISingleFingerDownHandler;
        // of course, this could search the whole scene,
        // just the local gameobject shown here for simplicity
        }

    ... when something happens ...

        if (needsUs != null) needsUs.OnSingleFingerDown(data);


    }

我让那个守护进程在某个地方运行。

如果您不是Unity用户 - 它的作用是环顾四周并找到任何ISingleFingerDownHandler个消费者,保留一个列表,然后根据需要适当地调用OnPointerDown。这工作正常但是

  • 消费者程序员必须记住“将守护进程放在某处”并使其运行等。

  • 每当你做这样的事情(在Unity或其他地方),效率,安置等等时,都有明显的反优势。

•如果一个消费者在守护程序没有搜索它们的时候出现这种方法,那么这种方法当然是失败的(Unity的神奇界面不会遇到这个问题 - 他们有更多的魔法来处理它)

(PS,我知道如何编写一个放置守护进程的自动助手等等:请不要那样回复,谢谢!)

事实上,很明显Unity的开发人员会在幕后进行一些系统,因为“他们的”接口完全能够调用所有需要的呼叫,无论是即时创建的项目等等,都可以完美地完成这一切。

什么是最好的解决方案?我需要一个守护进程吗?也许不得不注册?

(确实通常不会在典型的Unity项目中使用 - 只是让它成为一个继承的类;这种类型的设施自然是一个接口。)

所以回顾一下,Unity有这个:

public class LaraCroft:MonoBehaviour,IPointerDownHandler

当然,有一种方法可以让我替换,扩展......

public class LaraCroft:MonoBehaviour,ISuperiorPointerDownHandler

然后可以以相同的方式使用/分享该界面的神奇品质?我可以做得很好,但只有我做了一个守护进程。

更新

“ISingleFingerHandler”的完整解决方案“IPinchHandler”和Unity中的类似概念在这里:https://stackoverflow.com/a/40591301/294884

3 个答案:

答案 0 :(得分:4)

你说你不想做一个守护进程,但这正是Unity正在做的事情。添加UI组件 时自动添加的StandaloneInputModule类是该守护程序

enter image description here

你可以做的是创建一个新类,该类派生自一个派生自BaseInputModule的类(对于你的情况,可能是PointerInputModule),它可以处理触发器并提高额外事件然后添加EventSystem对象的新类。

请参阅Event System上的Unity手册部分,了解有关如何创建自定义事件的说明以及有关输入模块功能的更多详细信息。

答案 1 :(得分:2)

我不想回答我自己的问题,但答案是:

你不能。你必须添加一个守护进程。

但是,非常值得注意的是

确实, Unity添加一个守护程序 - 它们只是隐藏了一点。

理解的最后一个绝对关键点是:

Unity搞砸了:你实际上无法继承他们可爱的StandAloneInputModule。这是一个很大的错误。

Unity的StandAloneInputModule和IPointerDownHandler系列非常出色。但是你不能正确地继承他们。

事实是,你只需从IPointerDownHandler继承。这就是它的全部内容。

事实上你必须制作自己的守护进程(“好像”它继承自StandAloneInputModule),它实际上只是从IPointerDownHandler系列旁边。

所以实际答案是(A)你有这个

public class YourFingerClass:MonoBehaviour, IPinchHandler
    {
    public void OnPinchZoom (float delta)
        {
        _processPinch(delta);
        }

和(B)你必须把它放在一个游戏对象(它是一个守护进程)上,然后(C)它很容易最终处理捏等等。

PinchInputModule

就是这样!

{{1}}的完整生产代码...

https://stackoverflow.com/a/40591301/294884

...确实从(“使用”)IPointerDownHandler系列继承。

答案 2 :(得分:1)

我的假设是MonoBehaviour在ctor中运行类型检查。这就是为什么你不能在那些上使用ctor来避免重写那个过程的原因。常见的解决方案是您的接口还需要实现一个注册方法(例如Vuforia就是这样做),因此任何新实例都会自行注册。

您还可以使用自己的MB系统扩展MB类:

public class JoeMonoBehaviour : MonoBehaviour
{
     protected virtual void Awake(){
         Init();
     }
     private void Init(){
         if(this is ISuperiorPointerDownHandler)
         {
              if(ISuperiorHandler.Instance != null){
                   ISuperiorHandlerInstance.RegisterPointerDownHandler(this as ISuperiorPointerDownHandler);
              }
         }
     }
}

它没有Unity的魔力,但你无法用MonoBehaviour实现Unity的魔力。它需要子类确保它调用base.Awake(),如果覆盖它。

你必须想出自己的侧引擎系统才能运行自己的引擎逻辑。不确定这是值得的。

另一种解决方案是创建自己的实例化:

namespace JoeBlowEngine{
    public static GameObject Instantiate(GameObject prefab, Vector3 position, Quaternion rotation){
          GameObject obj = (GameObject)Instantiate(prefab, position, rotation);
          MonoBehaviour [] mbs = obj.GetComponentsInChildren<MonoBehaviour>(true); // I think it should also get all components on the parent object
          foreach(MonoBehaviour mb in mbs){
                CheckForSuperior(mb);
                CheckForInferior(mb);
                // so on...
          }
          return obj;
    }
    internal static CheckForSuperior(MonoBehaviour mb)
    {
         if(mb is SomeType) { SomeTypeHandler.Instance.Register(mb as SomeType); }
    }
}

现在看起来你只是做了一些魔术:

 JoeBlowEngine.Instantiate(prefab, Vector3.zero, Quaternion.identity);