前言
本篇为学习总结性质的文章,若有任何问题或错误,欢迎在评论区指出。 如果本文对您有一定帮助,也欢迎点赞、收藏、关注。
本文前置知识点:生命周期函数、事件、协程。
引入
有时,我们写了一个类,为了各种各样的原因,是不想或是不能继承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的统一管理,也可以在一定程度上降低由反射带来的性能开销。
|