游戏程序基础框架实例
单例管理者基类
不继承自Mono
1.利用单例模式和泛形去实现模块管理者基类;
public class SingletonBase<T> where T:new()
{
private static T instance;
public static T GetInstance()
{
if (instance == null) instance = new T();
return instance;
}
}
继承自Mono
1.直接实例化通过生成游戏对象并且附加脚本;
2.Awake实例化的话要保证单例的唯一性;
public class SingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T GetInstance()
{
if (instance == null)
{
GameObject go = new GameObject
{
name = typeof(T).Name,
};
instance = go.AddComponent<T>();
DontDestroyOnLoad(go);
}
return instance;
}
protected virtual void Awake()
{
if (instance == null)
{
instance = this as T;
DontDestroyOnLoad(gameObject);
}
}
}
事件管理者
1.观察者模式,利用了泛形和委托(事件)
2.这里的事件字典只能对付小游戏,大型游戏仍是不够
public interface IEventInfo { }
public class EventInfo : IEventInfo
{
public event UnityAction actions;
public EventInfo(UnityAction action)
{
actions += action;
}
public void EventTrigger()
{
actions?.Invoke();
}
}
public class EventInfo<T> : IEventInfo
{
public event UnityAction<T> actions;
public EventInfo(UnityAction<T> action)
{
actions += action;
}
public void EventTrigger(T Info)
{
actions?.Invoke(Info);
}
}
public class EventMgr : SingletonBase<EventMgr>
{
//暂时只定义一个字典进行储存,小游戏够用了
private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>();
public void AddListenter(string name,UnityAction action)
{
if (eventDic.TryGetValue(name, out IEventInfo eventInfo)) (eventInfo as EventInfo).actions += action;
else eventDic.Add(name,new EventInfo(action));
}
public void AddListenter<T>(string name, UnityAction<T> action)
{
if (eventDic.TryGetValue(name, out IEventInfo eventInfo)) (eventInfo as EventInfo<T>).actions += action;
else eventDic.Add(name, new EventInfo<T>(action));
}
public void RemoveListenter(string name, UnityAction action)
{
if (eventDic.TryGetValue(name, out IEventInfo eventInfo)) (eventInfo as EventInfo).actions -= action;
}
public void RemoveListenter<T>(string name, UnityAction<T> action)
{
if (eventDic.TryGetValue(name, out IEventInfo eventInfo)) (eventInfo as EventInfo<T>).actions -= action;
}
public void EventTrigger(string name)
{
if (eventDic.TryGetValue(name, out IEventInfo eventInfo)) (eventInfo as EventInfo).EventTrigger();
}
public void EventTrigger<T>(string name,T info)
{
if (eventDic.TryGetValue(name, out IEventInfo eventInfo)) (eventInfo as EventInfo<T>).EventTrigger(info);
}
public void Clear()
{
eventDic.Clear();
}
}
Mono根节点
1.可以创建一个全局唯一的Mono节点使用
2.实现游戏入口继承自该节点即可,但是实现Awake单例
public class MonoRoot : SingletonMono<MonoRoot>
{
private event UnityAction updateActions;
protected override void Awake()
{
base.Awake();
}
protected virtual void Update()
{
updateActions?.Invoke();
}
public void AddUpdateListenter(UnityAction updateAction)
{
updateActions += updateAction;
}
public void RemoveUpdateListenter(UnityAction updateAction)
{
updateActions -= updateAction;
}
}
资源加载管理者
1.资源加载有同步加载和异步加载,异步加载用协程实现
2.资源加载管理者包括场景切换,以及配置文件加载
public class ResMgr : SingletonBase<ResMgr>
{
#region 资源加载
private Dictionary<string,Object> ObjCacheDic=new Dictionary<string, Object>();
public T LoadObj<T>(string path , bool isIns=true , bool isCache= true ) where T : Object
{
T obj;
if (ObjCacheDic.TryGetValue(path, out Object cacheObj)) obj = cacheObj as T;
else
{
obj = Resources.Load<T>(path);
if (isCache) ObjCacheDic.Add(path, obj);
};
if (obj is GameObject && isIns) return GameObject.Instantiate(obj);
else return obj;
}
public void LoadObjAsyn<T>(string path,UnityAction<T> callback, bool isIns = true, bool isCache = true) where T : Object
{
if (ObjCacheDic.TryGetValue(path, out Object obj))
{
if (obj is GameObject && isIns)
callback(GameObject.Instantiate(obj) as T);
else
callback(obj as T);
}
else MonoRoot.GetInstance().StartCoroutine(ReallyLoadObjAsyn(path, callback, isIns, isCache));
}
public IEnumerator ReallyLoadObjAsyn<T>(string path, UnityAction<T> callback, bool isIns, bool isCache) where T : Object
{
ResourceRequest r = Resources.LoadAsync<T>(path);
yield return r;
if(isCache) ObjCacheDic.Add(path, r.asset);
if (r.asset is GameObject && isIns)
callback(GameObject.Instantiate(r.asset) as T);
else
callback(r.asset as T);
}
#endregion
#region 场景切换
public void LoadScene(string name, UnityAction fun)
{
//场景同步加载
SceneManager.LoadScene(name);
//加载完成过后 才会去执行fun
fun();
}
public void LoadSceneAsyn(string name, UnityAction fun)
{
MonoRoot.GetInstance().StartCoroutine(ReallyLoadSceneAsyn(name, fun));
}
public IEnumerator ReallyLoadSceneAsyn(string name, UnityAction fun)
{
AsyncOperation op = SceneManager.LoadSceneAsync(name);
while (!op.isDone)
{
//这里可以去做一些事情,例如更新进度条
//EventMgr.GetInstance().EventTrigger("更新进度条", op.progress);
yield return op.progress;
}
//加载完成过后 才会去执行fun
fun();
}
#endregion
}
缓存池管理者
1.用一个类类型去包装缓存队列,这个类里面应该封装了获取和设置缓存对象的方法
2.缓存池目前只支持预制物的缓存
public class PoolMgr :SingletonBase<PoolMgr>
{
private Dictionary<string, PoolData> poolDic=new Dictionary<string, PoolData>();
private GameObject poolRoot;
public void GetObj(string name, UnityAction<GameObject> callBack)
{
if(poolDic.TryGetValue(name, out PoolData poolData) && poolData.Count > 0)
{
callBack(poolData.GetObj());
}
else
{
//创建资源应使用资源路径
ResMgr.GetInstance().LoadObjAsyn<GameObject>(name, (go) =>{
go.name = name;
callBack(go);
});
}
}
public void PushObj(string name,GameObject obj)
{
if(poolRoot=null)poolRoot=new GameObject("PoolRoot");
if(poolDic.TryGetValue(name,out PoolData poolData)) poolData.PushObj(obj);
else poolDic.Add(name, new PoolData(obj,poolRoot));
}
public void Clear()
{
poolDic.Clear();
poolRoot = null;
}
}
public class PoolData
{
private GameObject parentRoot;
private Queue<GameObject> objQue;
public PoolData(GameObject obj,GameObject poolObj)
{
parentRoot = new GameObject($"{obj.name}Root");
parentRoot.transform.parent = poolObj.transform;
objQue = new Queue<GameObject>();
}
public int Count
{
get { return objQue.Count; }
}
public void PushObj(GameObject obj)
{
//存起来
objQue.Enqueue(obj);
//失活 让其隐藏
obj.SetActive(false);
//设置父对象
obj.transform.parent = parentRoot.transform;
}
public GameObject GetObj()
{
//取出第一个
GameObject obj = objQue.Dequeue();
//激活 让其显示
obj.SetActive(true);
//断开了父子关系
obj.transform.parent = null;
return obj;
}
}
输入管理者
1.输入管理者耦合事件监听和Mono根节点
public class InputMgr : SingletonBase<InputMgr>
{
private bool isStart = false;
public InputMgr()
{
MonoMgr.GetInstance().AddUpdateListener(UpdateIpt);
}
public void StartOrEndCheck(bool isOpen)
{
isStart = isOpen;
}
private void CheckKeyCode(KeyCode key)
{
//事件中心模块 分发按下抬起事件
if (Input.GetKeyDown(key))
EventCenter.GetInstance().EventTrigger("某键按下", key);
//事件中心模块 分发按下抬起事件
if (Input.GetKeyUp(key))
EventCenter.GetInstance().EventTrigger("某键抬起", key);
}
private void UpdateIpt()
{
//没有开启输入检测 就不去检测 直接return
if (!isStart)
return;
CheckKeyCode(KeyCode.W);
CheckKeyCode(KeyCode.S);
CheckKeyCode(KeyCode.A);
CheckKeyCode(KeyCode.D);
}
}
音效管理者
1.一般音效资源应该做缓存池处理
public class MusicMgr : MonoBehaviour
{
private bool isMute = false;
//背景音乐源
private AudioSource bkSource;
//背景音乐大小
private float bkValue = 1;
//一般音乐列表
private List<AudioSource> sourceLst = new List<AudioSource>();
//背景音乐大小
private float soundValue = 1;
//音乐源节点
private GameObject musicRoot;
public MusicMgr()
{
MonoMgr.GetInstance().AddUpdateListener(UpdateMsc);
}
//音乐播放完就回收
private void UpdateMsc()
{
for (int i = sourceLst.Count - 1; i >= 0; --i)
{
if (!sourceLst[i].isPlaying)
{
GameObject.Destroy(sourceLst[i]);
sourceLst.RemoveAt(i);
}
}
}
public void PlayBkMusic(string name)
{
if(!musicRoot) musicRoot=new GameObject("MusicRoot");
if (bkSource) bkSource = musicRoot.AddComponent<AudioSource>();
//异步加载背景音乐 加载完成后 播放
ResMgr.GetInstance().LoadObjAsyn<AudioClip>("Music/BK/" + name, (clip) =>
{
bkSource.clip = clip;
bkSource.loop = true;
bkSource.volume = bkValue;
bkSource.Play();
});
}
public void PauseBKMusic()
{
bkSource?.Pause();
}
public void StopBKMusic()
{
bkSource?.Stop();
}
public void ChangeBKValue(float v)
{
bkValue = v;
if (bkSource == null) bkSource.volume = bkValue;
}
public void PlaySound(string name, bool isLoop=false, GameObject parent=null, UnityAction<AudioSource> callBack = null)
{
if (!musicRoot) musicRoot = new GameObject("MusicRoot");
//当音效资源异步加载结束后 再添加一个音效
ResMgr.GetInstance().LoadObjAsyn<AudioClip>("Music/Sound/" + name, (clip) =>
{
AudioSource source;
if (parent) source=parent.AddComponent<AudioSource>();
else source = musicRoot.AddComponent<AudioSource>();
source.clip = clip;
source.loop = isLoop;
source.volume = soundValue;
source.Play();
sourceLst.Add(source);
if (callBack != null)
callBack(source);
});
}
public void ChangeSoundValue(float value)
{
soundValue = value;
for (int i = 0; i < sourceLst.Count; ++i)
sourceLst[i].volume = value;
}
public void StopSound(AudioSource source)
{
if (sourceLst.Contains(source))
{
sourceLst.Remove(source);
source.Stop();
GameObject.Destroy(source);
}
}
}
UI管理者和UI面板基类
面板基类
public class BasePanel : MonoBehaviour
{
//通过里式转换原则 来存储所有的控件
private Dictionary<string, List<UIBehaviour>> controlDic = new Dictionary<string, List<UIBehaviour>>();
protected virtual void Awake()
{
FindChildrenControl<Button>();
FindChildrenControl<Image>();
FindChildrenControl<Text>();
FindChildrenControl<Toggle>();
FindChildrenControl<Slider>();
FindChildrenControl<ScrollRect>();
FindChildrenControl<InputField>();
}
public virtual void ShowMe()
{
}
public virtual void HideMe()
{
}
protected virtual void OnClick(string btnName)
{
}
protected virtual void OnValueChanged(string toggleName, bool value)
{
}
protected T GetControl<T>(string controlName) where T : UIBehaviour
{
if (controlDic.ContainsKey(controlName))
{
for (int i = 0; i < controlDic[controlName].Count; ++i)
{
if (controlDic[controlName][i] is T)
return controlDic[controlName][i] as T;
}
}
return null;
}
private void FindChildrenControl<T>() where T : UIBehaviour
{
T[] controls = this.GetComponentsInChildren<T>();
for (int i = 0; i < controls.Length; ++i)
{
string objName = controls[i].gameObject.name;
if (controlDic.ContainsKey(objName))
controlDic[objName].Add(controls[i]);
else
controlDic.Add(objName, new List<UIBehaviour>() { controls[i] });
//如果是按钮控件
if (controls[i] is Button)
{
(controls[i] as Button).onClick.AddListener(() =>
{
OnClick(objName);
});
}
//如果是单选框或者多选框
else if (controls[i] is Toggle)
{
(controls[i] as Toggle).onValueChanged.AddListener((value) =>
{
OnValueChanged(objName, value);
});
}
}
}
}
UI管理者
public class UIMgr : SingletonBase<UIMgr>
{
public Dictionary<string, BasePanel> panelDic = new Dictionary<string, BasePanel>();
private Transform bot;
private Transform mid;
private Transform top;
private Transform system;
//记录我们UI的Canvas父对象 方便以后外部可能会使用它
public RectTransform canvas;
public UIMgr()
{
//创建Canvas 让其过场景的时候 不被移除
GameObject obj = ResMgr.GetInstance().LoadObj<GameObject>("UI/Canvas");
canvas = obj.transform as RectTransform;
GameObject.DontDestroyOnLoad(obj);
//找到各层
bot = canvas.Find("Bot");
mid = canvas.Find("Mid");
top = canvas.Find("Top");
system = canvas.Find("System");
//创建EventSystem 让其过场景的时候 不被移除
obj = ResMgr.GetInstance().LoadObj<GameObject>("UI/EventSystem");
GameObject.DontDestroyOnLoad(obj);
}
/// <summary>
/// 通过层级枚举 得到对应层级的父对象
/// </summary>
/// <param name="layer"></param>
/// <returns></returns>
public Transform GetLayerFather(EUIayer layer)
{
switch (layer)
{
case EUIayer.Bot:
return this.bot;
case EUIayer.Mid:
return this.mid;
case EUIayer.Top:
return this.top;
case EUIayer.System:
return this.system;
}
return null;
}
/// <summary>
/// 显示面板
/// </summary>
/// <typeparam name="T">面板脚本类型</typeparam>
/// <param name="panelName">面板名</param>
/// <param name="layer">显示在哪一层</param>
/// <param name="callBack">当面板预设体创建成功后 你想做的事</param>
public void ShowPanel<T>(string panelName, EUIayer layer = EUIayer.Mid, UnityAction<T> callBack = null) where T : BasePanel
{
if (panelDic.ContainsKey(panelName))
{
panelDic[panelName].ShowMe();
// 处理面板创建完成后的逻辑
if (callBack != null)
callBack(panelDic[panelName] as T);
//避免面板重复加载 如果存在该面板 即直接显示 调用回调函数后 直接return 不再处理后面的异步加载逻辑
return;
}
ResMgr.GetInstance().LoadObjAsyn<GameObject>("UI/" + panelName, (obj) =>
{
//把他作为 Canvas的子对象
//并且 要设置它的相对位置
//找到父对象 你到底显示在哪一层
Transform father = bot;
switch (layer)
{
case EUIayer.Mid:
father = mid;
break;
case EUIayer.Top:
father = top;
break;
case EUIayer.System:
father = system;
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;
//得到预设体身上的面板脚本
T panel = obj.GetComponent<T>();
// 处理面板创建完成后的逻辑
if (callBack != null)
callBack(panel);
panel.ShowMe();
//把面板存起来
panelDic.Add(panelName, panel);
});
}
/// <summary>
/// 隐藏面板
/// </summary>
/// <param name="panelName"></param>
public void HidePanel(string panelName)
{
if (panelDic.ContainsKey(panelName))
{
panelDic[panelName].HideMe();
GameObject.Destroy(panelDic[panelName].gameObject);
panelDic.Remove(panelName);
}
}
/// <summary>
/// 得到某一个已经显示的面板 方便外部使用
/// </summary>
public T GetPanel<T>(string name) where T : BasePanel
{
if (panelDic.ContainsKey(name))
return panelDic[name] as T;
return null;
}
/// <summary>
/// 给控件添加自定义事件监听
/// </summary>
/// <param name="control">控件对象</param>
/// <param name="type">事件类型</param>
/// <param name="callBack">事件的响应函数</param>
public static void AddCustomEventListener(UIBehaviour control, EventTriggerType type, UnityAction<BaseEventData> callBack)
{
EventTrigger trigger = control.GetComponent<EventTrigger>();
if (trigger == null)
trigger = control.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = type;
entry.callback.AddListener(callBack);
trigger.triggers.Add(entry);
}
}
/// <summary>
/// UI层级
/// </summary>
public enum EUIayer
{
Bot,
Mid,
Top,
System,
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 雪夜の自我救赎!
评论
ValineDisqus