最近很多功能都涉及到用戶設定相關的東西, 比如一個沙盤, 希望在無操作后3秒鐘就自動進行相機自動旋轉的操作, 代碼很簡單:
public class XXX : MonoBehaviour { public float cameraRotateSpeed = 2f; public float waitTime = 3f; private float m_startRunningTime = 0.0f; void Awake(){ ResetStartRunningTime(); } private void ResetStartRunningTime() { m_startRunningTime = Time.realtimeSinceStartup + waitTime; } private void Update() { if(Time.realtimeSinceStartup > m_startRunningTime) { var rotateAngle = cameraRotateSpeed * Time.deltaTime; // ... } } }
可是用戶如果想自己能設定這個功能的變數時, 我們可以怎么做呢?
1. 搞個快捷鍵? 各種功能都有用戶需求的話, 快捷鍵沒那么多. 幾十個快捷鍵沒人能記住.
2. 做個網頁后臺進行設定, 然后運行時從遠程獲取變數? 太麻煩, 而且依賴后臺, 而且還是異步的.
3. 在本地寫個json檔案配置表從里面讀取? 對開發不友好, 維護麻煩.
4. 做個UI面板運行時打開進行設定? 也是過于麻煩. 并且也需要存盤資料.
并且對于一個已經開發到一定程度的工程來說, 額外添加的這個需求不能在開發層面要求過多, 且不能影響原有功能的設計.
結果還是從本地檔案讀取結果最靠譜, 假設上面的代碼是單例或者使用時唯一, 那么就可以簡單的做個容器來存放基本資料就行了:
Dictionary<string, Common.DataTable>
string 就是成員變數名
Common.DataTable 就是變數
基本上是通過反射來獲取和設定變數的了, 不過在寫介面的時候, 希望能有硬連接而不是軟連接:
public class Person { public string Name { get; set; } } void Test(){ var name = nameof(Person.Name); }
這樣即使代碼修改了也會報錯, 不過工程用的還是C#4的語法, 只能通過運算式的方式來得到, 因為有Linq擴展可以寫成lambda的方式 :
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq.Expressions; using System.Reflection; namespace RuntimeData { using InternalModule.Common; internal class CompilerSerializedData { public string filePath { get; private set; } private bool inited = false; private Dictionary<string, Common.DataTable> m_datas = null; public CompilerSerializedData(string loadPath) { filePath = loadPath; } #region Main Funcs public Common.DataTable GetValue<T, TMem>(T host, Expression<System.Func<T, TMem>> key) where TMem : System.IConvertible { CheckInited(); Common.DataTable data = 0; var memberExpression = key.Body as MemberExpression; if(memberExpression != null && memberExpression.Member != null) { var memberInfo = memberExpression.Member; if(m_datas.TryGetValue(memberInfo.Name, out data) == false) { data.SetData(ReflectionHelper.GetValueFromMemberInfo<TMem>(host, memberInfo)); m_datas[memberInfo.Name] = data; } } return data; } public bool SetValue<T, TMem>(T host, Expression<System.Func<T, TMem>> key, TMem value) where TMem : System.IConvertible { CheckInited(); var memberExpression = key.Body as MemberExpression; if(memberExpression != null && memberExpression.Member != null) { var memberInfo = memberExpression.Member; Common.DataTable data = 0; data.SetData(value); m_datas[memberInfo.Name] = data; ReflectionHelper.SetValue(memberInfo, host, value); return true; } return false; } public void SaveToFile() { Common.JsonHelper.SaveJsonToFile(m_datas, filePath); } #endregion #region Main Funcs -- Static Support public Common.DataTable GetValue<TValue>(string name, System.Func<TValue> defaultValueFunc) where TValue : System.IConvertible { CheckInited(); Common.DataTable data = 0; if(m_datas.TryGetValue(name, out data) == false) { data.SetData(defaultValueFunc.Invoke()); m_datas[name] = data; } return data; } public bool TryGetValue(string name, out Common.DataTable dataTable) { CheckInited(); if(m_datas.TryGetValue(name, out dataTable)) { return true; } return false; } public void SetValue<TValue>(MemberInfo memberInfo, TValue value) where TValue : System.IConvertible { CheckInited(); Common.DataTable data = 0; if(m_datas.TryGetValue(memberInfo.Name, out data) == false) { data.SetData(value); m_datas[memberInfo.Name] = data; } } public bool SetValue<T, TMem>(Expression<System.Func<TMem>> key, TMem value) where TMem : System.IConvertible { CheckInited(); var memberExpression = key.Body as MemberExpression; if(memberExpression != null && memberExpression.Member != null) { var memberInfo = memberExpression.Member; Common.DataTable data = 0; data.SetData(value); m_datas[memberInfo.Name] = data; ReflectionHelper.SetValue(memberInfo, default(T), value); return true; } return false; } #endregion #region Help Funcs private void CheckInited() { if(false == inited) { inited = true; m_datas = Common.JsonHelper.LoadFromFile<Dictionary<string, Common.DataTable>>(filePath); if(m_datas == null) { m_datas = new Dictionary<string, Common.DataTable>(); } } } #endregion } public static class CompilerSerializedDatas { private static readonly Dictionary<System.Type, CompilerSerializedData> dictionary = new Dictionary<System.Type, CompilerSerializedData>(); public static Data.Common.PreCacheData<string> SavePathFolder = new Data.Common.PreCacheData<string>(() => { return Application.streamingAssetsPath + "/CompilerSerializedDatas"; }); #region Main Funcs public static Common.DataTable GetCompilerSerializedValue<T, TMem>(this T instance, Expression<System.Func<T, TMem>> key) where TMem : System.IConvertible { var data = https://www.cnblogs.com/tiancaiwrk/p/RequireCompilerSerializedData(typeof(T)); Common.DataTable dataTable = data.GetValue(instance, key); return dataTable; } public static bool SetCompilerSerializedValue<T, TMem>(this T instance, Expression<System.Func<T, TMem>> key, TMem value) where TMem : System.IConvertible { var data = https://www.cnblogs.com/tiancaiwrk/p/RequireCompilerSerializedData(typeof(T)); return data.SetValue(instance, key, value); } public static void Save() { foreach(var data in dictionary.Values) { data.SaveToFile(); } } #endregion #region Main Funcs -- Static Support private static Dictionary<string, object> CompiledFunc = new Dictionary<string, object>(); private static CompilerSerializedData RequireCompilerSerializedData(System.Type type) { CompilerSerializedData data = null; if(dictionary.TryGetValue(type, out data)) { return data; } RequireFolder(SavePathFolder.data); data = new CompilerSerializedData(string.Concat(SavePathFolder.data, "/", type.Name, ".json")); dictionary[type] = data; return data; } public static Common.DataTable GetCompilerSerializedValue<TValue>(Expression<System.Func<TValue>> expFunc) where TValue : System.IConvertible { var memberExpression = expFunc.Body as MemberExpression; if(memberExpression != null && memberExpression.Member != null) { var memberInfo = memberExpression.Member; var type = memberInfo.DeclaringType; var data =https://www.cnblogs.com/tiancaiwrk/p/ RequireCompilerSerializedData(type); Common.DataTable retVal = 0; if(data.TryGetValue(memberInfo.Name, out retVal)) { return retVal; } else { var value =https://www.cnblogs.com/tiancaiwrk/p/ CallExpression(expFunc); data.SetValue(memberInfo, value); retVal.SetData(value); } return retVal; } return 0; } public static bool SetCompilerSerializedValue<T, TValue>(Expression<System.Func<TValue>> expFunc, TValue value) where TValue : System.IConvertible { var data = https://www.cnblogs.com/tiancaiwrk/p/RequireCompilerSerializedData(typeof(T)); return data.SetValue<T, TValue>(expFunc, value); } private static bool MemberIsStatic(MemberInfo memberInfo) { var fieldInfo = memberInfo as FieldInfo; if(fieldInfo != null) { return (fieldInfo.IsStatic); } else { var propertyInfo = memberInfo as PropertyInfo; if(propertyInfo != null) { return (propertyInfo.GetAccessors(true)[0].IsStatic); } } return false; } private static System.Func<TValue> ExpressionToCall<TValue>(Expression<System.Func<TValue>> expFunc) where TValue : System.IConvertible { var key = expFunc.ToString(); var call = CompiledFunc.TryGetNullableValue(key) as System.Func<TValue>; if(call == null) { call = expFunc.Compile(); CompiledFunc[key] = call; } return call; } private static TValue CallExpression<TValue>(Expression<System.Func<TValue>> expFunc) where TValue : System.IConvertible { var call = ExpressionToCall(expFunc); return call.Invoke(); } #endregion #region Help Funcs private static bool RequireFolder(string folderPath) { if(System.IO.Directory.Exists(folderPath) == false) { var info = System.IO.Directory.CreateDirectory(folderPath); return info.Exists; } return true; } #endregion } }
通過代碼注入添加了序列化方法, 最初的代碼修改成:
public class XXX : MonoBehaviour { public float cameraRotateSpeed = 2f; public float waitTime = 3f; private float m_startRunningTime = 0.0f; void Awake(){ ResetStartRunningTime(); } private void ResetStartRunningTime() { m_startRunningTime = Time.realtimeSinceStartup + (float)this.GetCompilerSerializedValue(self => self.waitTime);; } private void Update() { if(Time.realtimeSinceStartup > m_startRunningTime) { var rotateAngle = (float)this.GetCompilerSerializedValue(self => self.cameraRotateSpeed) * Time.deltaTime; // ... } } }
這樣在第一次運行之后, 就可以自動生成json檔案了, 在啟動時會自動讀取檔案設定, 既然是用戶配置了, 那就不需要進行Set操作了, 不過以防萬一也提供了Set操作:
Tools.AutoCameraRoundController.instance.SetCompilerSerializedValue(self => self.waitTime, 100.0f);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/31121.html
標籤:其他
下一篇:如何判斷一個點在旋轉后的矩形中
