目录

  • 引言
  • 问题及解决过程
  • 总结


引言

在Unity中,继承MonoBehaviour的类(脚本)可以挂载在场景中的对象上,而且也不推荐继承了MonoBehaviour的类(脚本)在外部自己使用C#中的new关键字实例化。

Q1: 既然继承了MonoBehaviour的类(脚本)不推荐使用new关键字实例化,那么Unity内部是如何实例化这个类并将脚本挂载到场景中的对象上的呢?
A: 当我们使用C#语言发起了一个实例化组件的请求时,(1)Unity会在C++层创建一个对应的对象实例;(2)Unity的脚本后端(Mono或IL2CPP运行时)会在托管堆中创建脚本的实例;(3)Unity再将对象实例脚本实例桥接(对接),建立起了C#和C++间的映射;(4)绑定完成后,会将脚本实例注册到(register)核心循环(生命周期函数)。

Q2: 有没有一种方法让没有继承MonoBehaviour的类的方法也能够在生命周期函数中执行?
A: 当然有,接下来我要做的事情就是要实现这种情况。



问题及解决过程

既然是继承了MonoBehaviour类的对象才能够进入生命周期函数,那么我可以创建一个挂载了继承MonoBehaviour的类的对象到场景中,然后通过一个名为MonoMng的公共Mono模块来把(未继承MonoBehaviour的)脚本中的方法添加到继承了MonoBehaviour类的脚本中,然后再在其生命周期函数中选择执行这些方法。

这样讲有点绕,不妨分点列出,大致步骤如下:

  1. 创建一个继承MonoBehaviour的类,创建一个场景中的对象,把类挂载到对象上;
  2. 创建一个管理器类,负责:(1)把原本不可以添加到生命周期函数中的方法添加到在第一步创建的类中并在第一步创建的类中实现;(2)可以移除方法;(3)开启各种协程函数。

首先,我先创建一个类,继承MonoBehaviour的类,而且能够接收外部方法,同时在生命周期函数里执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MonoController : MonoBehaviour
{
//mono事件,接收外部来的方法
public event UnityAction monoEvent;

private void Update()
{
if(monoEvent != null)
{
monoEvent.Invoke();
}
else
{
Debug.Log("monoEvent为空,不存在来自外部的方法");
}
}

//添加监听函数的方法
public void AddUpdateListener(UnityAction func)
{
monoEvent += func;
}
//移除监听函数的方法
public void RemoveUpdateListener(UnityAction func)
{
monoEvent -= func;
}
//移除所有监听函数的方法
public void RemoveAllListeners()
{
monoEvent = null;
}
}

Q3: event是?
A: event是事件,声明一个事件的方式如此:修饰符+ event+委托类型+ 事件名
public event UnityAction myEvent;
在我看来,事件是加了更多限制的委托,在外部类,事件不能赋值、事件不能被调用,但是事件可以添加方法或者减少方法。这就是在内部类中的生命周期函数里调用事件的原因——不能在外部调用事件。

接下来,我创建一个MonoMng类,这是一个管理类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MonoMng : BaseMng<MonoMng>
{
//存储控制器的对象
public MonoController monoController;

public MonoMng()
{
//希望能够在场景中自动创建一个对象,然后添加MonoController脚本,再存储起来,方便以后使用
GameObject obj = new GameObject("MonoController");
monoController = obj.AddComponent<MonoController>();
}

//添加函数,再封装一遍,是因为外部使用的是MonoMng这个公共管理器类
public void AddUpdateListener(UnityAction func)
{
monoController.AddUpdateListener(func);
}
//移除函数
public void RemoveUpdateListener(UnityAction func)
{
monoController.RemoveUpdateListener(func);
}
//移除所有函数
public void RemoveAllListeners()
{
monoController.RemoveAllListeners();
}
}

Q4: 如何开启协程呢?
A: 如果在编辑器中选中MonoBehaviour并按下F12。可以找到几种开启协程的函数,我们可以在管理器类中封装一遍。这一步就交给你自己去做了,少年!

我先添加其中一个重载,剩下的也是同理:

1
2
3
4
5
//在MonoMng类中添加如下代码
public Coroutine StartCoroutine(string methodName)
{
return monoController.StartCoroutine(methodName);
}


总结

公共Mono模块做的事情:

  1. 在场景中创建一个对象,将MonoController脚本附在这个对象上,公共Mono模块中对象存储MonoController对象的实例;
  2. 外部想把方法添加到Update中或者想开启协程,但是又没继承MonoBehaviour类,可以用公共Mono模块把方法添加到MonoController里,就自然能够调用了。