本框架是我从github上clone下的,只能算是自己一些理解
若,若是侵权请联系我删除,若是有错误请指出
原项目地址
本框架包含了以下类
BaseManager、BasePanel、EventCenter、InputMgr、MonoController、MonoMger、MusicMgr、PoolMgr、ResMgr、UIManager
框架的作用便是协助开发,提升开发效率,但也不能一味的使用框架丧失了新功能的开发,不断学习,对自己的框架进行更行是程序员必须做的,下面我们就开始简单介绍这些框架
BaseManager最基础的类,大部分的类继承于它
此类为单例模式,项目中所有的使用单例模式的类都继承于它,并且此类使用了泛型,将大大减少重复性代码的出现
相信大家对单例模式都不陌生,就不注重细说了
//使用where对泛型T进行约束 public class BaseManagerMonoControllerwhere T:new() { private static T instance; //静态函数,建T的实体instance public static T GetInstance() { if (instance == null) instance = new T(); return instance; } //返回这个类的实体instance }
因为我们大部分类都继承自了BaseManager,而BaseManager没有继承MonoBehaviour,所以在这些类中我们无法使用Unity为我们提供的周期函数,所以我们定义了一个MonoController类让它实现周期函数
此框架为基本框架,如有需要亦可修改使用
public class MonoController : MonoBehaviour
{
//设置事件,用此事件监听需要循环更新执行的函数
private event UnityAction updateEvent;
private event UnityAction fixedUpdateEvent;
//此物体不可移除
private void Start()
{
DontDestroyOnLoad(this.gameObject);
}
//如果此事件不为空,则在此类的Update里调用
private void Update()
{
if (updateEvent != null)
updateEvent();
}
//随时间更行的调用
private void FixedUpdate()
{
if (fixedUpdateEvent != null)
fixedUpdateEvent();
}
//对方法添加监听
public void AddUpdateListener(UnityAction func)
{
updateEvent += func;
}
//移除
public void RemoveUpdateListener(UnityAction func)
{
updateEvent -= func;
}
public void AddFixedUpdateListener(UnityAction func)
{
fixedUpdateEvent += func;
}
public void RemoveFiUpdateListener(UnityAction func)
{
fixedUpdateEvent -= func;
}
}
MonoMgr
在拥有了MonoController后,我们需要解决的就是对MonoController的使用问题了,为此我们需要创建一个管理类MonoMgr,将此类继承自BaseManager做成单例模式,这样我们便能方便的调用MonoController了
using System.Collections; using System.Collections.Generic; using System.ComponentModel; using UnityEngine; using UnityEngine.Events; public class MonoMgr : BaseManagerResMgr{ //为了能够使用并管理MonoController,我们首先需要一个MonoController对象 private MonoController controller; //由于是单例模式,我们创建构造函数来进行一些必要的初始化 public MonoMgr() { //MonoController并不是单例模式,为了使用我们要让它在游戏中生成,首先新创建一个游戏物体名为MonoController GameObject obj = new GameObject("MonoController"); //挂载脚本,并获得此脚本的引用controller controller = obj.AddComponent (); } //调用controller里的添加监听的方法,注意传入的参数为无参无返回值的方法 public void AddUpdateListener(UnityAction func) { controller.AddUpdateListener(func); } //同上,不过此方法针对的是随时间更新 public void AddFixUpdateListener(UnityAction func) { controller.AddFixedUpdateListener(func); } //移除监听在Update里的方法 public void RemoveUpdeteListener(UnityAction func) { controller.RemoveUpdateListener(func); } //同上移除监听在FixUpdater的方法 public void RemoveFixUpdateListener(UnityAction func) { controller.RemoveFiUpdateListener(func); } //开启协程方法及其重载方法,使用时注意选择 public Coroutine StartCoroutine(IEnumerator routine) { return controller.StartCoroutine(routine); } public Coroutine StartCoroutine(string methodName, [DefaultValue("null")] object value) { return controller.StartCoroutine(methodName, value); } public Coroutine StartCoroutine(string methodName) { return controller.StartCoroutine(methodName); } }
资源加载类,通过Resource文件夹进行资源的加载
需要注意一点,此时加载出来的物体的this.name都会成为原名加上(clone)
所以在使用缓存池PoolMgr.PushObj时谨慎使用this.name
//单例模式继承自BaseManager,使用时可以通过GetInstance调用 public class ResMgr : BaseManagerPoolMgr{ //同步加载 //使用是要注意给出T的类型,如ResMagr.GetInstance().Load () public T Load (string objName) where T : Object { T res = Resources.Load (objName); //如果res是一个GameObject if (res is GameObject) { T t = GameObject.Instantiate(res); t.name = objName; return t; } else //else情况示例:TextAsset、AudioClip return res; } //异步加载,异步加载使用起来在观感上更加顺滑,适用于较大的资源 //异步加载使用协程 public void LoadAsync (string name, UnityAction callback) where T : Object { //由于是单例模式,要使用协程需要用到MonoMgr MonoMgr.GetInstance().StartCoroutine(ReallyLoadAsync (name, callback)); } private IEnumerator ReallyLoadAsync (string _name, UnityAction callback) where T : Object { ResourceRequest r = Resources.LoadAsync (name); yield return r;//直到系统读取完 if (r.asset is GameObject) { T t = GameObject.Instantiate(r.asset) as T; t.name = _name;//去点(clone)字段 callback(t);//callback方法作用很大,例如获得其组件脚本,改变其状态 } else r.asset.name = _name; callback(r.asset as T); } }
缓存池系统。系统频繁生成物体和销毁物体是很浪费性能的,为此我们可以将暂时用不到的物体使用SetActive(false)来隐藏掉,并将他们存入缓存池,在需要使用时在取出
public class PoolData
{
//缓存池中可能有多个不同种类的物体,为方便管理故需要设置一个父物体
public GameObject fatherObj;
//使用List链式结构来存储物体
public List poolList;
//构造函数,进行PoolData的一些初始化
public PoolData(GameObject obj,GameObject poolObj)
{
fatherObj = obj;
fatherObj.transform.parent = poolObj.transform;
poolList = new List();
PushObj(obj);
}
//将物体push进缓存池
public void PushObj(GameObject obj)
{
//存储、设置父物体、隐藏
poolList.Add(obj);
obj.transform.SetParent(fatherObj.transform);
obj.SetActive(false);
}
//将物体从缓存池取出
public GameObject GetObj()
{
GameObject obj = null;
obj = poolList[0];
poolList.RemoveAt(0);
obj.SetActive(true);
obj.transform.parent = null;
return obj;
}
}
//缓存池定义完成,我们需要一个对缓存池进行管理的单例模式类
public class PoolMgr : BaseManager
{
//使用字典存储数据
private Dictionary poolDic
= new Dictionary();
//缓存池的父物体
private GameObject poolObj;
//从缓存池中取出
public void GetObj(string objName, UnityAction callback)
{
//若缓存池中存在,取出
if (poolDic.ContainsKey(objName) && poolDic[objName].poolList.Count > 0)
callback(poolDic[objName].GetObj());
//若不存在则使用ResMgr动态加载
else
{
//注意生成的物体的this.name此时并不是objName,而是objName(clone),若是使用this.name进行PushObj的话,则无法使用objName从字典中进行Get
ResMgr.GetInstance().LoadAsync(objName,callback);
}
}
//将物体存如缓存池
//使用时注意objName,使用动态加载出来的物体的this.name会在原名后加上(clone)字样,此时使用this.name进行PushObj操作时,实际是创建了另一个池子,所以使用时推荐直接使用"objName"的方式而不是this.name的方式
public void PushObj(string objName, GameObject obj)
{
if (poolObj == null)
poolObj = new GameObject("Pool");//实例化,此后所以在缓存池的物体全部为其紫萼u提
if(poolDic.ContainsKey(objName))//如果缓存池中已经存在其类型,则将物体加入其中
poolDic[objName].PushObj(obj);
else//若缓存池中没有此类物体,则添加至字典
poolDic.Add(objName,new PoolData(obj,poolObj));
//我们采用的就结构是PoolData 类,里面含有链式结构PoolList
}
//清空缓存池
public void Clear()
{
poolDic.Clear();
poolObj = null;
}
}
EventCenter
事件中心,通过事件我们来解决各个函数的运行问题,减少脚本的耦合性,是结构桁架美观
事件系统的构成主要依赖于委托,委托没有学号的小伙伴最好先去学习下
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
//两个类使用此接口,可用此接口来调用对象,可使用as判断
public interface iEveninfo
{
}
//创建类继承自接口,以后便可以通过接口来引用此类
public class EventInfo : iEveninfo
{
//注意参数名actions,多加了一个S,此委托监听多个函数
public Action actions;
//action,将要监听的函数
public EventInfo(Action action)
{
actions += action;
}
}
//同上,不过此时的委托为无参委托
public class EventInfo : iEveninfo
{
public Action actions;
public EventInfo(Action action)
{
actions += action;
}
}
//事件系统为单例模式,继承自BaseManager
public class EventCenter : BaseManager
{
//使用字典结构,可以方便的通过“name"来触发委托,注意此时用的是接口
public Dictionary eventDic
= new Dictionary();
//添加监听,注意监听的函数为带参函数
public void AddEventListener(string eventNumber, Action action)
{
//如果字典中已经有此eventNumber,将此方法添加至字典中
if (eventDic.ContainsKey(eventNumber))
(eventDic[eventNumber] as EventInfo).actions += action;//使用as来转换,此时接口转化成了EventInfo
//若没有此eventNumber,则在字典中添加事件名和事件方法
else
eventDic.Add(eventNumber,new EventInfo(action));
}
//添加监听,同上不过方法无参数
public void AddEventListener(string eventNumber, Action action)
{
if (eventDic.ContainsKey(eventNumber))
(eventDic[eventNumber] as EventInfo).actions += action;
else
eventDic.Add(eventNumber,new EventInfo(action));
}
//触发监听的事件,由于存在泛型T,及我们的所监听的函数需要一个类型为T的参数才能触发
public void EventTrigger(string eventNumber, T info)
{
//如果字典里存在此事件并且actions不为空
if(eventDic.ContainsKey(eventNumber)&&
(eventDic[eventNumber] as EventInfo).actions!=null
)
(eventDic[eventNumber] as EventInfo).actions.Invoke(info);
}
//同上,但不需要参数
public void EventTrigger(string eventNumber)
{
if (eventDic.ContainsKey(eventNumber)&&
(eventDic[eventNumber] as EventInfo).actions != null)
(eventDic[eventNumber] as EventInfo).actions.Invoke();
}
//移除监听
public void RemoveEventListener(string eventNumber, Action action)
{
if (eventDic.ContainsKey(eventNumber))
(eventDic[eventNumber] as EventInfo).actions -= action;
}
//如上,移除监听
public void RemoveEventListener(string eventNumber, Action action)
{
if (eventDic.ContainsKey(eventNumber))
(eventDic[eventNumber] as EventInfo).actions -= action;
}
//清空字典
public void Clear()
{
eventDic.Clear();
}
}
InputMgr
输入系统,我们的对键盘鼠标的操作都可以在此进行定义,由于我们的操作要实时检测输入,所以在定义InputMgr时我们要使用到前面的MonoMgr
Unity2020版本 推出了新的Input System,有兴趣的小伙伴可以去学习,本文暂且不表
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; //继承自BaseManager public class InputMgr : BaseManagerBasePanel{ //输入开关 private bool isStart = false; private float timer; //构造函数,对设置初始化 public InputMgr() { MonoMgr.GetInstance().AddUpdateListener(MyUpDate);//帧更新,应该设置时间更新 MonoMgr.GetInstance().AddFixUpdateListener(MyFixUpdate); } public void StartOrEndCheck(bool isOpen) { isStart = isOpen; } private void MyUpDate() { if(!isStart) return; //在其中可设置你需要的输入方式,以下是我自己的定义 FindMousePoint();//找到鼠标的点,并实现事件 CheckHMove();//鼠标WS实现上下移动 CheckVMove();//鼠标AD实现左右移动 ChecKKeyCode(KeyCode.J)//实现按下J的事件监听 } //随时间更新 void MyFixUpdate() { if(!isStart) return; CheckMouseClick(0.12f); } //检测鼠标输入 void CheckMouseClick(float t) { timer += Time.deltaTime; if (Input.GetMouseButton(0) && timer >= t) { EventCenter.GetInstance().EventTrigger("Fire"); timer = 0; } } //鼠标输入,得到点击的位置 void FindMousePoint() { Vector3 point = Input.mousePosition; point.z = -10; point = Camera.main.ScreenToWorldPoint(point); Debug.Log(point); EventCenter.GetInstance().EventTrigger("FindMousePoint", point); } //检测WS键,执行VMove事件 public void CheckVMove() { float v = Input.GetAxisRaw("Vertical");//对于WS EventCenter.GetInstance().EventTrigger("VMove",v); } //检测AD键,执行HMove事件 public void CheckHMove() { float h = Input.GetAxisRaw("Horizontal");//对于AD EventCenter.GetInstance().EventTrigger("HMove",h); } //检测键盘输入,根据按压类别启动不同事件 public void CheckKeyCode(KeyCode key) { if (Input.GetKeyDown(key)) EventCenter.GetInstance().EventTrigger("KeyIsDown",key); if(Input.GetKey(key)) EventCenter.GetInstance().EventTrigger("KeyAlwaysDown",key); if(Input.GetKeyUp(key)) EventCenter.GetInstance().EventTrigger("KeyIsOpen",key); } }
UI的基础类,我们创建出的所有UI都可以继承自此类,当然也可以只选择为UI的父物体创建,用父物体的脚本来控制子物体的空间
BasePanel并不是管理,而是实现UI功能
public class BasePanel : MonoBehaviour
{
//使用字典存储UI名和其身上的控件
public Dictionary> controlDic
= new Dictionary>();
private void Awake()//在Start之前就得到下列
{
//或的控件并保存在字典中,可根据自己UI搭载的控件更改
FinChildControl
举例,我们创建一个Button,创建此脚本并给与Button,由于继承了BasePanel,Awak函数执行后就获得了Button组件,我们用GetControl得到此UI的Button,并注册事件
public class But : BasePanel
{
private Button b;//创建Button
private void Start()
{
GetControl
UIManager
UIManager作为UI管理类,我们能够使用它对UI进行管理,呼出UI、关闭UI,操作UI等,我们将UI层划分为三层,分别为Bot(底层)、Mid(中层)、Top(高层)
//设置枚举,表示层级
public enum E_UI_Layer
{
Bot,
Mid,
Top,
}
//继承自BaseManager,单例模式随时调用
public class UIManager : BaseManager
{ //用字典存储给UI,方便管理
public Dictionary panelDic
= new Dictionary();
private Transform bot;
private Transform mid;
private Transform top;
//构造函数,用于初始化
public UIManager()
{ //利用ResMgr加载画布
GameObject obj = ResMgr.GetInstance().Load("UI/Canvas");
Transform canvas = obj.transform;
GameObject.DontDestroyOnLoad(obj);//UI不随场景切换销毁
bot = canvas.Find("bot");
mit = canvas.Find("mid");
top = canvas.Find("top");
//UI系统没有EventSystem无法启动
obj = ResMgr.GetInstance().Load("UI/EventSystem");
GameObject.DontDestroyOnLoad(obj);
}
//将此Panel移动到Top层,并且callback委托可传可不传函数
public void ShowPanel(string panelName,
E_UI_Layer layer = E_UI_Layer.Top,
UnityAction callback = null) where T : BasePanel
{
//若字典中有注册过此面板
if (panelDic.ContainsKey(panelName))
{
panelDic[panelName].ShowMe();
if (callback != null)
callback(panelDic[panelName] as T);
return;
}
//若字典中未注册过此面板,从Resource文件夹中加载
//使用了lambda表达式
ResMgr.GetInstance().LoadAsync("UI/"+panelName,(obj)=>
{
//把它作为Canvas的子对象
//并且设置它的相对位置
//找到父对象
Transform father = bot;
switch (layer) {
case E_UI_Layer.Mit:
father = mit;
break;
case E_UI_Layer.Top:
father = top;
break;
}
//设置父对象
obj.transform.SetParent(father);
//设置相对位置和大小
obj.transform.localPosition = Vector3.zero;
obj.transform.localScale = Vector3.one;
(obj.transform as RectTransform).offsetMax = Vector2.zero;
(obj.transform as RectTransform).offsetMin = Vector2.zero;
//得到预设体身上的脚本(继承自BasePanel)
T panel = obj.GetComponent();
//执行外面想要做的事情
if (callback != null) {
callback(panel);
}
//在字典中添加此面板
panelDic.Add(panelName, panel);
});
}
public void HidePanel(string panelName)
{
if (panelDic[panelName])
{
panelDic[panelName].HideMe();
//个人感觉没必要
panelDic[panelName].HideMe();
GameObject.Destroy(panelDic[panelName].gameObject);
panelDic.Remove(panelName);
}
}
}
MusicMgr
音乐音效管理类,都是些基础,大家应该都能看懂吧
public class MusicMgr : BaseManager{ private AudioSource bkMusic = null; private float bkValue = 1; private float soundValue = 1; private GameObject soundObj = null; private List soundList = new List(); public MusicMgr() { MonoMgr.GetInstance().AddUpdateListener(update); } private void update() { for (int i = soundList.Count - 1; i >= 0; i--) { if (!soundList[i].isPlaying) { GameObject.Destroy(soundList[i]); soundList.RemoveAt(i); } } } public void PlayBKMusic(string musciName) { if (bkMusic == null) { GameObject obj = new GameObject("BKMusic"); bkMusic = obj.AddComponent(); } ResMgr.GetInstance().LoadAsync("Music/bk" + musciName, (clip) => { bkMusic.clip = clip; bkMusic.loop = true; bkMusic.volume = bkValue; bkMusic.Play(); }); } public void ChangeBKValue(float v) { bkValue = v; if(bkMusic==null) return; bkMusic.volume = bkValue; } public void PauseBKMusic() { if(bkMusic==null) return; bkMusic.Pause(); } public void StopBkMusic() { if(bkMusic==null) return; bkMusic.Stop(); } public void PlaySound(string musicName, bool isLoop, UnityAction callback = null) { if (soundObj == null) { soundObj = new GameObject(); soundObj.name = "Sounds"; } AudioSource source = soundObj.AddComponent(); ResMgr.GetInstance().LoadAsync("Music/Sounds/"+musicName, (clip) => { source.clip = clip; source.loop = isLoop; source.volume = soundValue; source.Play(); if (callback != null) callback(source); }); } public void ChangeSoundValue(float value) { soundValue = value; for (int i = 0; i < soundList.Count; i++) soundList[i].volume = value; } public void StopSound(AudioSource source) { if (soundList.Contains(source)) { soundList.Remove(source); source.Stop(); GameObject.Destroy(source); } } }



