文章目录
前言
有些情况下我们的脚本可能是没法继承MonoBehaviour的,比如状态机里的状态类,场景系统异步加载时的协程等。
Mono管理器可以实现在不继承MonoBehaviour的类能够开启协程,并且可以将需要在Update、LateUpdate、FixedUpdate实际执行的逻辑托管给它。
一、Mono管理器的原理
-
1、在场景中创建一个继承MonoBehaviour的“执行者”脚本,这个脚本就专门
用来开启协程和监听帧更新。 -
2、Mono管理器访问这个“执行者”脚本,就可以实现所需的效果。
二、实现
1、新增Mono执行者
/// <summary>
/// Mono执行者
/// </summary>
public class MonoController : MonoSingleton<MonoController>
{
}
2、新增Mono管理器
/// <summary>
/// Mono管理器
/// </summary>
public class MonoManager : NonMonoSingleton<MonoManager>
{
// 私有构造方法,防止外部 new 对象
private MonoManager() { }
private MonoController MonoController => MonoController.Instance;
/// <summary>
/// 开启协程
/// </summary>
public Coroutine StartCoroutine(IEnumerator routine)
{
return MonoController.StartCoroutine(routine);
}
}
新增测试脚本,触发调用携程
public class Text : MonoBehaviour {
private void Start() {
//开启携程
MonoManager.Instance.StartCoroutine(MyCoroutine());
}
IEnumerator MyCoroutine()
{
while (true){
Debug.Log("携程执行中");
yield return null;
}
}
}
效果
3、停止协程
修改MonoManager
/// <summary>
/// 停止某个协程
/// </summary>
public void StopCoroutine(IEnumerator routine)
{
MonoController.StopCoroutine(routine);
}
/// <summary>
/// 停止某个协程
/// </summary>
public void StopCoroutine(Coroutine routine)
{
MonoController.StopCoroutine(routine);
}
/// <summary>
/// 停止所有协程
/// </summary>
public void StopAllCoroutines()
{
MonoController.StopAllCoroutines();
}
测试调用
public class Text : MonoBehaviour {
Coroutine coroutine;
private void OnGUI() {
if(GUI.Button(new Rect(0, 0, 100, 50), "开启协程")){
coroutine = MonoManager.Instance.StartCoroutine(MyCoroutine());
}
if(GUI.Button(new Rect(0, 80, 100, 50), "停止协程")){
MonoManager.Instance.StopCoroutine(coroutine);
}
if(GUI.Button(new Rect(0, 160, 100, 50), "停止所有协程")){
MonoManager.Instance.StopAllCoroutines();
}
}
IEnumerator MyCoroutine()
{
while (true){
Debug.Log("携程执行中");
yield return null;
}
}
}
效果
4、Update
修改MonoController
/// <summary>
/// Mono执行者
/// </summary>
public class MonoController : MonoSingleton<MonoController>
{
//在生命周期方法Update中执行的事件
event UnityAction updateEvent;
void Update() {
updateEvent?.Invoke();
}
public void AddUpdateListener(UnityAction call)
{
updateEvent += call;
}
public void RemoveUpdateListener(UnityAction call)
{
updateEvent -= call;
}
public void RemoveAllUpdateListeners()
{
updateEvent = null;
}
}
修改MonoManager
/// <summary>
/// 添加Update事件
/// </summary>
public void AddUpdateListener(UnityAction call)
{
MonoController.AddUpdateListener(call);
}
/// <summary>
/// 移除某个Update事件
/// </summary>
public void RemoveUpdateListener(UnityAction call)
{
MonoController.RemoveUpdateListener(call);
}
/// <summary>
/// 移除所有Update事件
/// </summary>
public void RemoveAllUpdateListeners()
{
MonoController.RemoveAllUpdateListeners();
}
测试调用
public class Text : MonoBehaviour {
private void OnGUI() {
if(GUI.Button(new Rect(100, 0, 120, 50), "开启Update")){
MonoManager.Instance.AddUpdateListener(OnUpdate);
}
if(GUI.Button(new Rect(100, 80, 120, 50), "停止Update")){
MonoManager.Instance.RemoveUpdateListener(OnUpdate);
}
if(GUI.Button(new Rect(100, 160, 120, 50), "停止所有Update")){
MonoManager.Instance.RemoveAllUpdateListeners();
}
}
void OnUpdate(){
Debug.Log("update执行中");
}
}
效果
5、fixedupdate
修改MonoController
event UnityAction fixedUpdateEvent;
void FixedUpdate() {
fixedUpdateEvent?.Invoke();
}
public void AddFixedUpdateListener(UnityAction call)
{
fixedUpdateEvent += call;
}
public void RemoveFixedUpdateListener(UnityAction call)
{
fixedUpdateEvent -= call;
}
public void RemoveAllFixedUpdateListeners()
{
fixedUpdateEvent = null;
}
修改MonoManager
/// <summary>
/// 添加FixedUpdate事件
/// </summary>
public void AddFixedUpdateListener(UnityAction call)
{
MonoController.AddFixedUpdateListener(call);
}
/// <summary>
/// 移除某个FixedUpdate事件
/// </summary>
public void RemoveFixedUpdateListener(UnityAction call)
{
MonoController.RemoveFixedUpdateListener(call);
}
/// <summary>
/// 移除所有FixedUpdate事件
/// </summary>
public void RemoveAllFixedUpdateListeners()
{
MonoController.RemoveAllFixedUpdateListeners();
}
测试调用
public class Text : MonoBehaviour {
private void OnGUI() {
if(GUI.Button(new Rect(220, 0, 150, 50), "开启FixedUpdate")){
MonoManager.Instance.AddFixedUpdateListener(OnFixedUpdate);
}
if(GUI.Button(new Rect(220, 80, 150, 50), "停止FixedUpdate")){
MonoManager.Instance.RemoveFixedUpdateListener(OnFixedUpdate);
}
if(GUI.Button(new Rect(220, 160, 150, 50), "停止所有FixedUpdate")){
MonoManager.Instance.RemoveAllFixedUpdateListeners();
}
}
void OnFixedUpdate(){
Debug.Log("FixedUpdate执行中");
}
}
效果
6、lateUpdate
修改MonoController
event UnityAction lateUpdateEvent;//在生命周期方法LateUpdate中执行的事件
void LateUpdate() {
lateUpdateEvent?.Invoke();
}
public void AddLateUpdateListener(UnityAction call)
{
lateUpdateEvent += call;
}
public void RemoveLateUpdateListener(UnityAction call)
{
lateUpdateEvent -= call;
}
public void RemoveAllLateUpdateListeners()
{
lateUpdateEvent = null;
}
修改MonoManager
/// <summary>
/// 添加LateUpdate事件
/// </summary>
public void AddLateUpdateListener(UnityAction call)
{
MonoController.AddLateUpdateListener(call);
}
/// <summary>
/// 移除某个LateUpdate事件
/// </summary>
public void RemoveLateUpdateListener(UnityAction call)
{
MonoController.RemoveLateUpdateListener(call);
}
/// <summary>
/// 移除所有LateUpdate事件
/// </summary>
public void RemoveAllLateUpdateListeners()
{
MonoController.RemoveAllLateUpdateListeners();
}
测试调用
public class Text : MonoBehaviour {
private void OnGUI() {
if(GUI.Button(new Rect(370, 0, 150, 50), "开启LateUpdate")){
MonoManager.Instance.AddLateUpdateListener(OnLateUpdate);
}
if(GUI.Button(new Rect(370, 80, 150, 50), "停止LateUpdate")){
MonoManager.Instance.RemoveLateUpdateListener(OnLateUpdate);
}
if(GUI.Button(new Rect(370, 160, 150, 50), "停止所有LateUpdate")){
MonoManager.Instance.RemoveAllLateUpdateListeners();
}
}
void OnLateUpdate(){
Debug.Log("LateUpdate执行中");
}
}
效果
7、直接停止协程报错
如果不开启协程,直接停止协程会报错
NullReferenceException: routine is null
修改MonoManager,添加routine
空判断
/// <summary>
/// 停止某个协程
/// </summary>
public void StopCoroutine(IEnumerator routine)
{
if(routine != null) MonoController.StopCoroutine(routine);
}
/// <summary>
/// 停止某个协程
/// </summary>
public void StopCoroutine(Coroutine routine)
{
if(routine != null) MonoController.StopCoroutine(routine);
}
8、Mono管理器优化,使用嵌套类
目前我们可以直接在物体上挂载MonoController组件
将MonoManager和MonoController脚本合二为一
目前外部可以不通过MonoManager
,而是直接访问monoController
,这显然应该是我们不允许的
且可以直接进行挂载使用
修改MonoController,新增私有构造方法,防止外部实例化调用对象
private MonoController() { }
结果,再想从外部直接实例化调用就报错了
三、最终代码
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Mono管理器
/// </summary>
public class MonoManager : NonMonoSingleton<MonoManager>
{
// 私有构造方法,防止外部new对象
private MonoManager() { }
// private MonoController monoExecuter;
private MonoController MonoController => MonoController.Instance;
// {
// get {
// if(monoExecuter == null) {
// GameObject go = new GameObject(typeof(MonoController).Name);
// monoExecuter = go.AddComponent<MonoController>();
// }
// return monoExecuter;
// }
// }
#region 协程
/// <summary>
/// 开启协程
/// </summary>
public Coroutine StartCoroutine(IEnumerator routine)
{
return MonoController.StartCoroutine(routine);
}
/// <summary>
/// 停止某个协程
/// </summary>
public void StopCoroutine(IEnumerator routine)
{
if(routine != null) MonoController.StopCoroutine(routine);
}
/// <summary>
/// 停止某个协程
/// </summary>
public void StopCoroutine(Coroutine routine)
{
if(routine != null) MonoController.StopCoroutine(routine);
}
/// <summary>
/// 停止所有协程
/// </summary>
public void StopAllCoroutines()
{
MonoController.StopAllCoroutines();
}
#endregion
#region Update
/// <summary>
/// 添加Update事件
/// </summary>
public void AddUpdateListener(UnityAction call)
{
MonoController.AddUpdateListener(call);
}
/// <summary>
/// 移除某个Update事件
/// </summary>
public void RemoveUpdateListener(UnityAction call)
{
MonoController.RemoveUpdateListener(call);
}
/// <summary>
/// 移除所有Update事件
/// </summary>
public void RemoveAllUpdateListeners()
{
MonoController.RemoveAllUpdateListeners();
}
#endregion
#region FixedUpdate
/// <summary>
/// 添加FixedUpdate事件
/// </summary>
public void AddFixedUpdateListener(UnityAction call)
{
MonoController.AddFixedUpdateListener(call);
}
/// <summary>
/// 移除某个FixedUpdate事件
/// </summary>
public void RemoveFixedUpdateListener(UnityAction call)
{
MonoController.RemoveFixedUpdateListener(call);
}
/// <summary>
/// 移除所有FixedUpdate事件
/// </summary>
public void RemoveAllFixedUpdateListeners()
{
MonoController.RemoveAllFixedUpdateListeners();
}
#endregion
#region LateUpdate
/// <summary>
/// 添加LateUpdate事件
/// </summary>
public void AddLateUpdateListener(UnityAction call)
{
MonoController.AddLateUpdateListener(call);
}
/// <summary>
/// 移除某个LateUpdate事件
/// </summary>
public void RemoveLateUpdateListener(UnityAction call)
{
MonoController.RemoveLateUpdateListener(call);
}
/// <summary>
/// 移除所有LateUpdate事件
/// </summary>
public void RemoveAllLateUpdateListeners()
{
MonoController.RemoveAllLateUpdateListeners();
}
#endregion
}
/// <summary>
/// Mono执行者
/// </summary>
public class MonoController : MonoSingleton<MonoController>
{
event UnityAction updateEvent;//在生命周期方法Update中执行的事件
event UnityAction fixedUpdateEvent;//在生命周期方法FixedUpdate中执行的事件
event UnityAction lateUpdateEvent;//在生命周期方法LateUpdate中执行的事件
private MonoController() { }
#region Update
void Update() {
updateEvent?.Invoke();
}
public void AddUpdateListener(UnityAction call)
{
updateEvent += call;
}
public void RemoveUpdateListener(UnityAction call)
{
updateEvent -= call;
}
public void RemoveAllUpdateListeners()
{
updateEvent = null;
}
#endregion
#region FixedUpdate
void FixedUpdate() {
fixedUpdateEvent?.Invoke();
}
public void AddFixedUpdateListener(UnityAction call)
{
fixedUpdateEvent += call;
}
public void RemoveFixedUpdateListener(UnityAction call)
{
fixedUpdateEvent -= call;
}
public void RemoveAllFixedUpdateListeners()
{
fixedUpdateEvent = null;
}
#endregion
#region LateUpdate
void LateUpdate() {
lateUpdateEvent?.Invoke();
}
public void AddLateUpdateListener(UnityAction call)
{
lateUpdateEvent += call;
}
public void RemoveLateUpdateListener(UnityAction call)
{
lateUpdateEvent -= call;
}
public void RemoveAllLateUpdateListeners()
{
lateUpdateEvent = null;
}
#endregion
}
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~