前言

本篇为学习总结性质的文章,若有任何问题或错误,欢迎在评论区指出。
如果本文对您有一定帮助,也欢迎点赞、收藏、关注。

本文前置知识点:生命周期函数、事件、协程。


目录

  • 前言
  • 引入
  • 思路
  • 公共Mono控制器
  • 公共Mono管理器
  • 总结


引入

有时,我们写了一个类,为了各种各样的原因,是不想或是不能继承MonoBehaviour的。但同时,我们又想使用帧更新函数或是使用协程,这时该怎么办?
一个比较好的方法是写一个公共Mono模块,令其继承MonoBehaviour并挂载在相应对象上,同时保证其可以全局访问
这样,便可以将需要每帧执行的代码“放入”其中,或是通过它开启协程。如此,不继承MonoBehaviour的类也可以通过调用这个模块,以满足使用Update()或是协程的需求。

思路

公共Mono控制器

“引入”中其实已经说得很清楚了,创建一个继承MonoBehaviour的类,给外部提供可添加帧更新和开启协程的方法,如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events; 	//为了使用Unity自带委托

/// <summary>
/// 公共Mono模块控制器
/// </summary>
public class MonoController : MonoBehaviour
{
	//无参委托,存储需要每帧更新的方法们
    private event UnityAction updateEvent;

    void Start()
    {
    	//过场景不销毁
        DontDestroyOnLoad(this.gameObject);
    }

    void Update()
    {
        if(updateEvent != null)
        {
        	//不为空则执行
            updateEvent();
        }
    }

    //添加更新函数
    public void AddUpdateListener(UnityAction fun)
    {
        updateEvent += fun;
    }

    //移除更新函数,注意适时调用以防止内存泄漏
    public void RemoveUpdateListener(UnityAction fun)
    {
        updateEvent -= fun;
    }
}

完成了以上,其实就大致能用了。
想要通过其使用协程,找到挂载对象,对象.GetComponent<MonoController>().StartCoroutine(IEnumerator routine)即可。

但显然有一个问题,就是这个MonoController显然用起来不太方便
我们首先需要将其挂载到场景中的一个物体上,然后每次需要往其中新添加需要每帧更新的函数时,都不得不去找到这个物体,获取其上的MonoController,再调用方法添加。这太过繁琐了。
既然是一个方便统筹管理的工具模块,我们其实就应该将其封装得好用易理解。一个方案是再写一个单例模式的公共Mono管理器,实现全局访问,顺便解决需要初始手动创建的问题。

公共Mono管理器

核心思路也比较简单:在其构造函数上做文章,使其初始创建空对象并创建、挂载MonoController,“存储”该MonoController,即可再次封装并做到在其中添加帧更新函数、通过其开启协程。
代码如下:

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// 公共Mono模块管理器
/// </summary>
public class MonoMgr
{ 
	//单例模式
	private static MonoMgr instance;

    public static MonoMgr GetInstance()
    {
        if (instance == null)
            instance = new MonoMgr();
        return instance;
    }

	//公共Mono模块控制器
    private MonoController controller;

    public MonoMgr()
    {
        //通过单例模式构造函数在场景中创建唯一的公共Mono模块控制器
        GameObject obj = new GameObject("MonoController");
        controller = obj.AddComponent<MonoController>();
    }

    /// <summary>
    /// 使用公共Mono模块添加帧更新函数
    /// </summary>
    /// <param name="fun">对应的每帧所需执行的方法</param>
    public void AddUpdateListener(UnityAction fun)
    {
        //向公共Mono模块控制器中添加更新函数
        controller.AddUpdateListener(fun);
    }

    /// <summary>
    /// 使用公共Mono模块移除帧更新函数
    /// </summary>
    /// <param name="fun">对应的每帧所需执行的方法</param>
    public void RemoveUpdateListener(UnityAction fun)
    {
        //向公共Mono模块控制器中移除更新函数
        controller.RemoveUpdateListener(fun);
    }

    /// <summary>
    /// 通过公共Mono模块开启协程
    /// </summary>
    /// <param name="fun">所需开启协程的迭代器</param>
    public Coroutine StartCoroutine(IEnumerator routine)
    {
        //通过公共Mono模块控制器开启协程
        return controller.StartCoroutine(routine);
    }

}

完成了以上,即可通过MonoMgr.GetInstance()实现全局访问,点出对应方法即可使用。
根据需求的不同,以上代码也可修改或继续完善。

总结

公共Mono模块由继承了MonoBehaviour、真正实现帧更新和协程等的MonoController单例模式实现初始创建和全局访问的MonoMgr组成。
有了公共Mono模块,我们可以实现不继承MonoBehaviour的类也可实现帧更新、使用协程。同时,公共Mono模块也可以做到Update的统一管理,也可以在一定程度上降低由反射带来的性能开销