基础程序框架
前言
完成所有项目都有的公共模块。使用这些框架可以做一些小项目或者毕业设计。
一、为什么要做这些
1、公共模块可以简单的理解为整个程序框架,提升开发效率
2、这些模块在游戏中各处都会用到
3、往往这些模块在各个小项目中是通用的,完成一次后可以通用。
二、包含内容
1.单例模式基类
单例模式可以减少重复代码的书写
普通单例基类
/// <summary>
/// 1、C# 泛型知识点
/// 2、设计模式中 单例模式
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingleBase<T> where T : new()
{
private static T intance;
public static T Instance
{
get
{
if (Instance == null)
intance = new T();
return Instance;
}
}
}
继承Mono的基类 需要在Awake 中初始化
/// <summary>
/// 继承MonoBehaviour的 单例模式 对象 自己去保证他的唯一性
/// </summary>
public class SingleMonoBase<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
//继承来Mono的脚本 不能直接new
//只能通过拖动到对象身上 或者 通过 加载脚本的api AddComponent 去加脚本 unity 内部帮我们去实例化它
public static T Instance => instance;
protected virtual void Awake()
{
instance = this as T;
}
}
继承mono基类的 升级
/// <summary>
///继承这种自动创建的 单例模式基类 不需要我们手动去拖 api去加
///想用时 直接Instance 就行
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingleAutoMonoBase<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
GameObject obj = new GameObject("");
//设置对象的名字为脚本名
obj.name = typeof(T).ToString();
//让单例模式对象 过场景 不移除
//因为 单例模式对象 往往 是存在整个程序声明周期中的
GameObject.DontDestroyOnLoad(obj);
instance = obj.AddComponent<T>();
}
return instance;
}
}
}
2.缓存池模块
资源循环利用 节约性能 减少cpu和内存消耗
/// <summary>
///池子中的一列数据 (抽屉数据
/// </summary>
public class PoolData
{
/// <summary>
/// 抽屉中 对象挂载的父节点 (抽屉名)
/// </summary>
public GameObject fatherObj;
/// <summary>
/// 对象的容器 (抽屉的容器)
/// </summary>
public List<GameObject> poolList;
/// <summary>
///
/// </summary>
/// <param name="obj">需要放入抽屉的物品</param>
/// <param name="poolobj">衣柜</param>
public PoolData(GameObject obj, GameObject poolobj)
{
//给抽屉创建一个父对象 并且把它作为poolobj(衣柜的子物体)
fatherObj = new GameObject(obj.name);
fatherObj.transform.parent = poolobj.transform;
poolList = new List<GameObject>();
PushObj(obj);
}
/// <summary>
/// 往抽屉里面放东西
/// </summary>
/// <param name="obj"></param>
public void PushObj(GameObject obj)
{
//存起来
poolList.Add(obj);
//设置父对象
obj.transform.parent = fatherObj.transform;
//失活
obj.gameObject.SetActive(false);
}
/// <summary>
/// 抽屉里面拿物品
/// </summary>
/// <returns></returns>
public GameObject GetObj()
{
GameObject obj;
obj = poolList[0];//拿抽屉里面的第一个物品
poolList.RemoveAt(0);//然后从抽屉中移除第一个物品
obj.transform.parent = null;
obj.gameObject.SetActive(true);
return obj;
}
}
/// <summary>
/// 缓存池管理器(比喻成一个衣柜)
/// </summary>
//缓存池 管理器 管理所有的对象
//由于管理器都是 唯一的 所以需要写成单例模式
public class PoolManager : SingleBase<PoolManager>
{
/// <summary>
/// key : 缓存池中 物品的名称(衣柜中的抽屉的名称)
/// value:缓存池中的物品 (抽屉)
/// </summary>
public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
/// <summary>
/// 缓存池中所有对象的父节点
/// </summary>
private GameObject poolObj;
/// <summary>
/// 从对象池中取出物品(从衣柜中拿东西 参数就是物品的名字)
/// </summary>
/// <param name="name">根据这个名字取出对应的物品</param>
/// <returns></returns>
public GameObject GetObj(string name)
{
GameObject obj = null;
//有抽屉 并且抽屉里面有东西
if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count > 0)
{
obj = poolDic[name].GetObj();
}
else
{
//直接实例化一个物体
obj = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>(name));
obj.name = name;
}
return obj;
}
/// <summary>
/// 暂时不用的东西放回对象池(不用的东西 放进衣柜里面)
/// </summary>
/// <param name="name">抽屉的名称(放到哪一个抽屉)</param>
/// <param name="obj">具体的物品是什么</param>
public void PushObj(string name, GameObject obj)
{
if (poolObj == null)
poolObj = new GameObject("Pool");
//里面有抽屉
if (poolDic.ContainsKey(name))
{
//得到该抽屉 然后把物品放进去
poolDic[name].PushObj(obj);
}
else
//里面没有该抽屉
{
PoolData list = new PoolData(obj, poolObj);
//赋值: 若不存在该Key, 添加新元素, 若已存在key, 重新赋值
poolDic[name] = list;
}
}
/// <summary>
/// 清空缓存池 主要用于 场景切换
/// </summary>
public void Clear()
{
poolDic.Clear();
poolObj = null;
}
}
3.事件中心模块
减低程序耦合性 减少代码复杂度
//里氏转换原则 来避免装箱拆箱
public interface IEventInfo { }//空接口
public class EventInfo<T> : IEventInfo
{
public UnityAction<T> actions;
public EventInfo(UnityAction<T> action)
{
actions += action;
}
}
public class EventInfo : IEventInfo
{
public UnityAction actions;
public EventInfo(UnityAction action)
{
actions += action;
}
}
/// <summary>
/// 事件中心
/// </summary>
public class EventCenter : SingleBase<EventCenter>
{
/// <summary>
/// key:事件的名字
/// vslue: 对应的是监听该事件的委托方法(父类装子类)
/// </summary>
private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>();
/// <summary>
/// 监听事件(带泛型参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="action">用来处理该事件的方法</param>
public void EventListenner<T>(string eventName, UnityAction<T> action)
{
//有没有对应的事件监听
//有
if (eventDic.ContainsKey(eventName))
{
//委托 一对多
(eventDic[eventName] as EventInfo<T>).actions += action;
}
else//没有
{
eventDic[eventName] = new EventInfo<T>(action);
}
}
/// <summary>
/// 监听事件不带参数
/// </summary>
/// <param name="eventName"></param>
/// <param name="action"></param>
public void EventListenner(string eventName, UnityAction action)
{
//有没有对应的事件监听
//有
if (eventDic.ContainsKey(eventName))
{
//委托 一对多
(eventDic[eventName] as EventInfo).actions += action;
}
else//没有
{
eventDic[eventName] = new EventInfo(action);
}
}
/// <summary>
/// 事件触发(带泛型参数)
/// </summary>
/// <param name="eventName">那个名字的事件触发了</param>
public void EventTrigger<T>(string eventName, T info)
{
if (eventDic.ContainsKey(eventName))
{
// eventDic[eventName]?.Invoke(info);
if ((eventDic[eventName] as EventInfo<T>).actions != null)
(eventDic[eventName] as EventInfo<T>).actions(info);//执行委托函数
}
}
/// <summary>
/// 事件触发(不带泛型参数)
/// </summary>
/// <param name="eventName"></param>
public void EventTrigger(string eventName)
{
if (eventDic.ContainsKey(eventName))
{
// eventDic[eventName]?.Invoke(info);
if ((eventDic[eventName] as EventInfo).actions != null)
(eventDic[eventName] as EventInfo).actions();//执行委托函数
}
}
/// <summary>
/// 移除对应事件(事件有加就有减 不然会出问题)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="action">对应之间添加的委托函数</param>
public void RemoveEvent<T>(string eventName, UnityAction<T> action)
{
if (eventDic.ContainsKey(eventName))
{
(eventDic[eventName] as EventInfo<T>).actions -= action;
}
}
/// <summary>
/// 不带参数的
/// </summary>
/// <param name="eventName"></param>
/// <param name="action"></param>
public void RemoveEvent(string eventName, UnityAction action)
{
if (eventDic.ContainsKey(eventName))
{
(eventDic[eventName] as EventInfo).actions -= action;
}
}
}
4.公共Mono模块
让没有继承Mono的类可以开启协程,可以Update更新,统一管理Update
/// <summary>
/// Mono管理器
/// </summary>
public class MonoController : MonoBehaviour
{
public event UnityAction updataEvent;
private void Start()
{
DontDestroyOnLoad(this.gameObject);
}
private void Update()
{
if (updataEvent != null)
updataEvent();
}
/// <summary>
/// 提供给外部添加帧跟新事件
/// </summary>
/// <param name="fun"></param>
public void AddUpdateListener(UnityAction fun)
{
updataEvent += fun;
}
/// <summary>
/// 移除帧更新事件
/// </summary>
/// <param name="fun"></param>
public void RemoveUpdateListener(UnityAction fun)
{
updataEvent -= fun;
}
}
/// <summary>
/// 可以提供给外部添加帧更新的方法
/// </summary>
public class MonoManager : SingleBase<MonoManager>
{
private MonoController monoController;
public MonoManager()
{
GameObject obj = new GameObject("monoController");
monoController = obj.AddComponent<MonoController>();
}
public void AddUpdateListener(UnityAction fun)
{
monoController.AddUpdateListener(fun);
}
public void RemoveUpdateListener(UnityAction fun)
{
monoController.RemoveUpdateListener(fun);
}
/// <summary>
/// 开启携程方法
/// </summary>
/// <param name="enumerator"></param>
/// <returns></returns>
public Coroutine StartCoroutine(IEnumerator enumerator)
{
return monoController.StartCoroutine(enumerator); ;
}
}
5.场景切换模块、
提供场景切换的公共接口
/// <summary>
/// 场景切换
/// </summary>
public class SceneMgr : SingleBase<SceneMgr>
{
/// <summary>
/// 同步切换场景
/// </summary>
/// <param name="name"></param>
public void LoadScene(string name, UnityAction action)
{
//场景同步加载 (肯能会出现卡顿)
SceneManager.LoadScene(name);
//场景加载完后 执行加载完后需要执行从方法
action();
}
/// <summary>
/// 提供给外部的异步加载方法
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
public void LoadSceneAsyn(string name, UnityAction action)
{
//开启协程
MonoManager.Instance.StartCoroutine(ReallyLoadSceneAsyn(name, action));
}
/// <summary>
/// 协程异步加载场景
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
/// <returns></returns>
public IEnumerator ReallyLoadSceneAsyn(string name, UnityAction action)
{
AsyncOperation async = SceneManager.LoadSceneAsync(name);
//async.progress 场景加载的进度
while (!async.isDone)//判断是否加载完成
{
//事件中心 派发进度条更新事件
EventCenter.Instance.EventTrigger("进度条", async.progress);
//进来一次 就返回一次进度 (更新进度条)
yield return async.progress;
}
yield return async;
//加载完后
action();
}
}
6.资源加载模块
提供资源加载的公共接口
/// <summary>
/// 资源管理器
/// </summary>
public class ResManager : SingleBase<ResManager>
{
/// <summary>
/// 同步加载资源
/// </summary>
/// <param name="name"></param>
public T Load<T>(string name) where T : Object
{
T res = Resources.Load<T>(name);
//如果资源是GameObjct 实例化后在返回出去
if (res is GameObject)
return GameObject.Instantiate(res);
else
return res;
}
//异步加载
public void LoadResAsync<T>(string name, UnityAction<T> callback) where T : Object
{
//开启异步加载协程
MonoManager.Instance.StartCoroutine(ReallyLoadAsyn(name, callback));
}
/// <summary>
/// 协调程序函数 开启资源异步加载
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name">资源名字</param>
/// <param name="callback">资源加载完后回调函数 </param>
/// <returns></returns>
private IEnumerator ReallyLoadAsyn<T>(string name, UnityAction<T> callback) where T : Object
{
ResourceRequest request = Resources.LoadAsync<T>(name);
yield return request;
if (request.asset is GameObject)
{
callback(GameObject.Instantiate(request.asset) as T);
}
else
{
callback(request.asset as T);
}
}
}
7.输入控制模块
PC端键盘输入管理Input输入相关逻辑
/// <summary>
/// 输入管理类
/// </summary>
public class InputManager : SingleBase<InputManager>
{
private bool isStart = false;
/// <summary>
/// 可以自动修改 输入键
/// </summary>
private KeyCode setCode = KeyCode.A;
/// <summary>
/// 构造函数中 添加input 监听
/// </summary>
public InputManager()
{
MonoManager.Instance.AddUpdateListener(MyUpdata);
}
private void MyUpdata()
{
//没有开启输入检测 就不去检测
if (!isStart)
return;
CheackKey(KeyCode.W);
CheackKey(setCode);
CheackKey(KeyCode.S);
CheackKey(KeyCode.D);
}
/// <summary>
/// 用来检测某键按下抬起
/// </summary>
/// <param name="code"></param>
private void CheackKey(KeyCode code)
{
if (Input.GetKeyDown(code))
//事件中心 分发按下事件 在需要的地方去监听该事件即可
EventCenter.Instance.EventTrigger<KeyCode>("按下", code);
if (Input.GetKeyUp(code))
EventCenter.Instance.EventTrigger<KeyCode>("抬起", code);
}
/// <summary>
/// 是否开启或关闭输入检测
/// </summary>
public void StarOrStopCheack(bool isOpen)
{
isOpen = !isOpen;
}
事件监听
public void Start()
{
StarOrStopCheack(true);
//监听某键按下
EventCenter.Instance.EventListenner<KeyCode>("按下", CheackInputDown);
EventCenter.Instance.EventListenner<KeyCode>("抬起", CheackInputUp);
}
private void CheackInputUp(KeyCode key)
{
switch (key)
{
case KeyCode.W:
Debug.Log("前进");
break;
case KeyCode.S:
Debug.Log("后退");
break;
case KeyCode.A:
Debug.Log("向左");
break;
case KeyCode.D:
Debug.Log("向右");
break;
}
}
private void CheackInputDown(KeyCode key)
{
switch (key)
{
case KeyCode.W:
Debug.Log("前进");
break;
case KeyCode.S:
Debug.Log("后退");
break;
case KeyCode.A:
Debug.Log("向左");
break;
case KeyCode.D:
Debug.Log("向右");
break;
}
}
8.音效管理模块
统一管理音乐音效相关
/// <summary>
/// 音效管理器
/// </summary>
public class MusicManager : SingleBase<MusicManager>
{
private AudioSource bkMusic = null;
/// <summary>
/// 背景音乐大小
/// </summary>
private float bkvalue = 1;
/// <summary>
/// 音效大小
/// </summary>
private float soundvalue = 1;
private GameObject soundObj = null;
private List<AudioSource> audiosList = new List<AudioSource>();
public MusicManager()
{
MonoManager.Instance.AddUpdateListener(Updata);
}
private void Updata()
{
for (int i = audiosList.Count - 1; i >= 0; i--)
{
if (!audiosList[i].isPlaying)
{
audiosList.RemoveAt(i);
GameObject.Destroy(audiosList[i]);
}
}
}
/// <summary>
/// 播放背景音效
/// </summary>
/// <param name="name"></param>
public void PlayBackMusic(string name)
{
if (bkMusic == null)
{
GameObject obj = new GameObject("bkMusic");
bkMusic = obj.AddComponent<AudioSource>();
}
//异步加载背景音乐 加载完成后 播放
ResManager.Instance.LoadResAsync<AudioClip>("Music/BK/" + name, (clip) =>
{
bkMusic.clip = clip;
bkMusic.loop = true;
bkMusic.volume = bkvalue;
bkMusic.Play();
});
}
/// <summary>
/// 暂停背景音效
/// </summary>
public void PauseBackMusic()
{
if (bkMusic == null)
return;
bkMusic.Pause();
}
/// <summary>
/// 改变背景音效大小
/// </summary>
/// <param name="value"></param>
public void SetSoundVaule(float value)
{
bkvalue = value;
if (bkMusic == null)
return;
bkMusic.volume = bkvalue;
}
/// <summary>
/// 停止背景音乐
/// </summary>
public void StopBackMusic()
{
if (bkMusic == null)
return;
bkMusic.Stop();
}
/// <summary>
/// 播放音效
/// </summary>
/// <param name="name"></param>
public void PlaySound(string name, bool isloop, UnityAction<AudioSource> callback = null)
{
if (soundObj == null)
{
soundObj = new GameObject();
soundObj.name = "Sound";
}
//音效资源异步加载结束后 再添加一个音效
ResManager.Instance.LoadResAsync<AudioClip>("Sound/" + name, (clip) =>
{
AudioSource audio = soundObj.AddComponent<AudioSource>();
audio.clip = clip;
audio.volume = soundvalue;
audio.loop = isloop;
audio.Play();
audiosList.Add(audio);
//如果外面想要使用这个音效 就传入一个回调函数来接它
if (callback != null)
callback(audio);
});
}
public void ChangeSoundValue(float value)
{
soundvalue = value;
for (int i = 0; i < audiosList.Count; i++)
{
audiosList[i].volume = soundvalue;
}
}
/// <summary>
/// </summary>
/// 停止音效
public void StopSoun(AudioSource source)
{
if (audiosList.Contains(source))
{
audiosList.Remove(source);
source.Stop();
GameObject.Destroy(source);
}
}
}
9.UI模块
统一管理UI面板的显示相关(UGUI)
UI基类面板
/// <summary>
///UI面板基类
/// </summary>
public class UIPanelBase : MonoBehaviour
{
//找到自身的所有的子控件
private Dictionary<string, List<UIBehaviour>> controllerDic = new Dictionary<string, List<UIBehaviour>>();
private void Awake()
{
FindControlInChildren<Button>();
FindControlInChildren<Text>();
FindControlInChildren<Toggle>();
FindControlInChildren<ScrollRect>();
FindControlInChildren<Slider>();
}
public T GetControl<T>(string controllname) where T : UIBehaviour
{
if (controllerDic.ContainsKey(controllname))
{
for (int i = 0; i < controllerDic.Count; i++)
{
if (controllerDic[controllname][i] is T)
return controllerDic[controllname][i] as T;
}
}
return null;
}
/// <summary>
/// 找到子对象的对应空间
/// </summary>
/// <typeparam name="T"></typeparam>
private void FindControlInChildren<T>() where T : UIBehaviour
{
T[] contoller = this.GetComponentsInChildren<T>();
for (int i = 0; i < contoller.Length; i++)
{
if (controllerDic.ContainsKey(contoller[i].gameObject.name))
controllerDic[contoller[i].gameObject.name].Add(contoller[i]);
else
controllerDic.Add(contoller[i].gameObject.name, new List<UIBehaviour>() { contoller[i] });
}
}
/// <summary>
/// 显示自己
/// </summary>
public virtual void ShowMe()
{
}
/// <summary>
/// 隐藏自己
/// </summary>
public virtual void HideMe()
{
}
}
UI管理面板
/// <summary>
/// UI层级
/// </summary>
public enum E_UI_Layer
{
Bot,
Mid,
Top,
System,
}
/// <summary>
/// UI管理器
/// </summary>
public class UiManager : SingleBase<UiManager>
{
public Dictionary<string, UIPanelBase> panelDic = new Dictionary<string, UIPanelBase>();
private Transform bot;
private Transform mid;
private Transform top;
private Transform system;
//记录我们UI的Canvas父对象 方便以后外部可能会使用它
public RectTransform canvas;
public UiManager()
{
//创建Canvas 让其过场景的时候 不被移除
GameObject obj = ResManager.Instance.Load<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 = ResManager.Instance.Load<GameObject>("UI/EventSystem");
GameObject.DontDestroyOnLoad(obj);
}
/// <summary>
/// 通过层级枚举 得到对应层级的父对象
/// </summary>
/// <param name="layer"></param>
/// <returns></returns>
public Transform GetLayerFather(E_UI_Layer layer)
{
switch (layer)
{
case E_UI_Layer.Bot:
return this.bot;
case E_UI_Layer.Mid:
return this.mid;
case E_UI_Layer.Top:
return this.top;
case E_UI_Layer.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, E_UI_Layer layer = E_UI_Layer.Mid, UnityAction<T> callBack = null) where T : UIPanelBase
{
if (panelDic.ContainsKey(panelName))
{
panelDic[panelName].ShowMe();
// 处理面板创建完成后的逻辑
if (callBack != null)
callBack(panelDic[panelName] as T);
//避免面板重复加载 如果存在该面板 即直接显示 调用回调函数后 直接return 不再处理后面的异步加载逻辑
return;
}
ResManager.Instance.LoadResAsync<GameObject>("UI/" + panelName, (obj) =>
{
//把他作为 Canvas的子对象
//并且 要设置它的相对位置
//找到父对象 你到底显示在哪一层
Transform father = bot;
switch (layer)
{
case E_UI_Layer.Mid:
father = mid;
break;
case E_UI_Layer.Top:
father = top;
break;
case E_UI_Layer.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 : UIPanelBase
{
if (panelDic.ContainsKey(name))
return panelDic[name] as T;
return null;
}
}
10.数据管理模块
统一管理数据存储相关
/// <summary>
/// PlayerPrefs数据管理类 统一管理数据的存储和读取
/// </summary>
public class PlayerPrefsDataMgr
{
private static PlayerPrefsDataMgr instance = new PlayerPrefsDataMgr();
public static PlayerPrefsDataMgr Instance
{
get
{
return instance;
}
}
private PlayerPrefsDataMgr()
{
}
/// <summary>
/// 存储数据
/// </summary>
/// <param name="data">数据对象</param>
/// <param name="keyName">数据对象的唯一key 自己控制</param>
public void SaveData( object data, string keyName )
{
//就是要通过 Type 得到传入数据对象的所有的 字段
//然后结合 PlayerPrefs来进行存储
#region 第一步 获取传入数据对象的所有字段
Type dataType = data.GetType();
//得到所有的字段
FieldInfo[] infos = dataType.GetFields();
#endregion
#region 第二步 自己定义一个key的规则 进行数据存储
//我们存储都是通过PlayerPrefs来进行存储的
//保证key的唯一性 我们就需要自己定一个key的规则
//我们自己定一个规则
// keyName_数据类型_字段类型_字段名
#endregion
#region 第三步 遍历这些字段 进行数据存储
string saveKeyName = "";
FieldInfo info;
for (int i = 0; i < infos.Length; i++)
{
//对每一个字段 进行数据存储
//得到具体的字段信息
info = infos[i];
//通过FieldInfo可以直接获取到 字段的类型 和字段的名字
//字段的类型 info.FieldType.Name
//字段的名字 info.Name;
//要根据我们定的key的拼接规则 来进行key的生成
//Player1_PlayerInfo_Int32_age
saveKeyName = keyName + "_" + dataType.Name +
"_" + info.FieldType.Name + "_" + info.Name;
//现在得到了Key 按照我们的规则
//接下来就要来通过PlayerPrefs来进行存储
//如何获取值
//info.GetValue(data)
//封装了一个方法 专门来存储值
SaveValue(info.GetValue(data), saveKeyName);
}
PlayerPrefs.Save();
#endregion
}
private void SaveValue(object value, string keyName)
{
//直接通过PlayerPrefs来进行存储了
//就是根据数据类型的不同 来决定使用哪一个API来进行存储
//PlayerPrefs只支持3种类型存储
//判断 数据类型 是什么类型 然后调用具体的方法来存储
Type fieldType = value.GetType();
//类型判断
//是不是int
if( fieldType == typeof(int) )
{
//为int数据加密
int rValue = (int)value;
rValue += 10;
PlayerPrefs.SetInt(keyName, rValue);
}
else if (fieldType == typeof(float))
{
PlayerPrefs.SetFloat(keyName, (float)value);
}
else if (fieldType == typeof(string))
{
PlayerPrefs.SetString(keyName, value.ToString());
}
else if (fieldType == typeof(bool))
{
//自己顶一个存储bool的规则
PlayerPrefs.SetInt(keyName, (bool)value ? 1 : 0);
}
//如何判断 泛型类的类型呢
//通过反射 判断 父子关系
//这相当于是判断 字段是不是IList的子类
else if( typeof(IList).IsAssignableFrom(fieldType) )
{
//父类装子类
IList list = value as IList;
//先存储 数量
PlayerPrefs.SetInt(keyName, list.Count);
int index = 0;
foreach (object obj in list)
{
//存储具体的值
SaveValue(obj, keyName + index);
++index;
}
}
//判断是不是Dictionary类型 通过Dictionary的父类来判断
else if( typeof(IDictionary).IsAssignableFrom(fieldType) )
{
//父类装自来
IDictionary dic = value as IDictionary;
//先存字典长度
PlayerPrefs.SetInt(keyName, dic.Count);
//遍历存储Dic里面的具体值
//用于区分 表示的 区分 key
int index = 0;
foreach (object key in dic.Keys)
{
SaveValue(key, keyName + "_key_" + index);
SaveValue(dic[key], keyName + "_value_" + index);
++index;
}
}
//基础数据类型都不是 那么可能就是自定义类型
else
{
SaveData(value, keyName);
}
}
/// <summary>
/// 读取数据
/// </summary>
/// <param name="type">想要读取数据的 数据类型Type</param>
/// <param name="keyName">数据对象的唯一key 自己控制</param>
/// <returns></returns>
public object LoadData( Type type, string keyName )
{
//不用object对象传入 而使用 Type传入
//主要目的是节约一行代码(在外部)
//假设现在你要 读取一个Player类型的数据 如果是object 你就必须在外部new一个对象传入
//现在有Type的 你只用传入 一个Type typeof(Player) 然后我在内部动态创建一个对象给你返回出来
//达到了 让你在外部 少写一行代码的作用
//根据你传入的类型 和 keyName
//依据你存储数据时 key的拼接规则 来进行数据的获取赋值 返回出去
//根据传入的Type 创建一个对象 用于存储数据
object data = Activator.CreateInstance(type);
//要往这个new出来的对象中存储数据 填充数据
//得到所有字段
FieldInfo[] infos = type.GetFields();
//用于拼接key的字符串
string loadKeyName = "";
//用于存储 单个字段信息的 对象
FieldInfo info;
for (int i = 0; i < infos.Length; i++)
{
info = infos[i];
//key的拼接规则 一定是和存储时一模一样 这样才能找到对应数据
loadKeyName = keyName + "_" + type.Name +
"_" + info.FieldType.Name + "_" + info.Name;
//有key 就可以结合 PlayerPrefs来读取数据
//填充数据到data中
info.SetValue(data, LoadValue(info.FieldType, loadKeyName));
}
return data;
}
/// <summary>
/// 得到单个数据的方法
/// </summary>
/// <param name="fieldType">字段类型 用于判断 用哪个api来读取</param>
/// <param name="keyName">用于获取具体数据</param>
/// <returns></returns>
private object LoadValue(Type fieldType, string keyName)
{
//根据 字段类型 来判断 用哪个API来读取
if( fieldType == typeof(int) )
{
//解密 减10
return PlayerPrefs.GetInt(keyName, 0) - 10;
}
else if (fieldType == typeof(float))
{
return PlayerPrefs.GetFloat(keyName, 0);
}
else if (fieldType == typeof(string))
{
return PlayerPrefs.GetString(keyName, "");
}
else if (fieldType == typeof(bool))
{
//根据自定义存储bool的规则 来进行值的获取
return PlayerPrefs.GetInt(keyName, 0) == 1 ? true : false;
}
else if( typeof(IList).IsAssignableFrom(fieldType) )
{
//得到长度
int count = PlayerPrefs.GetInt(keyName, 0);
//实例化一个List对象 来进行赋值
//用了反射中双A中 Activator进行快速实例化List对象
IList list = Activator.CreateInstance(fieldType) as IList;
for (int i = 0; i < count; i++)
{
//目的是要得到 List中泛型的类型
list.Add(LoadValue(fieldType.GetGenericArguments()[0], keyName + i));
}
return list;
}
else if( typeof(IDictionary).IsAssignableFrom(fieldType) )
{
//得到字典的长度
int count = PlayerPrefs.GetInt(keyName, 0);
//实例化一个字典对象 用父类装子类
IDictionary dic = Activator.CreateInstance(fieldType) as IDictionary;
Type[] kvType = fieldType.GetGenericArguments();
for (int i = 0; i < count; i++)
{
dic.Add(LoadValue(kvType[0], keyName + "_key_" + i),
LoadValue(kvType[1], keyName + "_value_" + i));
}
return dic;
}
else
{
return LoadData(fieldType, keyName);
}
}
}
原文地址:https://blog.csdn.net/qq_41860752/article/details/115795540
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。