【unity进阶知识2】Mono管理器,在不继承MonoBehaviour情况下调用MonoBehaviour里的功能,比如协程、Update、LateUpdate、FixedUpdate

前言

有些情况下我们的脚本可能是没法继承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

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向宇it

创作不易,感谢你的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值