作者:韩铁衣 | 来源:互联网 | 2024-11-19 19:22
序列化相关问题:https://blog.uwa4d.com/archives/2025.html
一直想研究,怎么使用 ScriptableObject 来存储多态数组,目前U3d 中序列化是不允许的。
第一种尝试
思想:把多态类型的基类继承自ScriptableObject ,然后属性面板界面自己实现:
[Serializable]
public class StoryEffect:ScriptableObject
{protected string _Name = "StoryEffect";protected string _EffectBasePath = "StoryNode/StoryCanvas/";[SerializeField]protected StorySegment.StoryState _ExecState = StorySegment.StoryState.NONE;private bool _IsPlaying = false;public bool IsPlaying {get { return _IsPlaying; }}public StorySegment.StoryState ExecState{get { return _ExecState; }}public string Name{get { return _Name; }}public virtual IEnumerator Init(){Debug.Log("StoryEffect:Init______________________");yield return null;}public virtual IEnumerator Play(){Debug.Log("StoryEffect:Play______________________");_IsPlaying = true;yield return null;}}public class StorySegment : ScriptableObject,IStorySegment
{public enum StoryState{NOnE= 0,START = 1,DOING = 2,END = 3,}[SerializeField]private int _ID = 0;[SerializeField]private List _StoryEffectList = new List();private StoryState _CurState = StoryState.NONE;private bool _IsEnd = false;}public override void OnInspectorGUI(){serializedObject.Update();// 绘制全部原有属性//base.DrawDefaultInspector();//重绘数组界面EditorGUILayout.PropertyField(this.serializedObject.FindProperty("_ID"), true);_expand = EditorGUILayout.Foldout(_expand, "StoryEffectList");if (_expand){EditorGUI.indentLevel++;for (int i = 0; i <_target.GetStoryEffectCount(); ++i){var ttt = _target.GetStoryEffectByIndex(i);var subopen = false;if (!_subExpand.TryGetValue(i, out subopen)){subopen = false;}GUILayout.BeginHorizontal();if (GUILayout.Button("删", GUILayout.Width(20), GUILayout.Height(20))){_target.RemoveStoryEffectByIndex(i);return;}_subExpand[i] = EditorGUILayout.Foldout(subopen, ttt.Name);GUILayout.EndHorizontal();if (subopen){EditorGUI.indentLevel++;Type t = ttt.GetType();FieldInfo[] fieldInfos = t.GetFields(BindingFlags.Instance|BindingFlags.Static | BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Default);//Debug.Log("fieldName------>" +fieldInfos.Length);PropertyInfo[] ps = t.GetProperties(BindingFlags.Instance|BindingFlags.Static | BindingFlags.Public|BindingFlags.DeclaredOnly);foreach (var p in ps){string pName = p.Name;string pType = p.PropertyType.ToString();object pValue = p.GetValue(ttt,null);if (p.CanWrite){DisStoryEffectPropertyInfo(pName, pType, pValue, p, ttt);}}foreach (var f in fieldInfos){//字段名称string fieldName = f.Name;//字段类型string fieldType = f.FieldType.ToString();//字段的值object fieldValue = f.GetValue(ttt);if (f.GetCustomAttributes(typeof(SerializeField), true).Length > 0){DisStoryEffectFieldInfo(fieldName, fieldType, fieldValue, f, ttt);}// Debug.Log("fieldName------>" + fieldName );
// Debug.Log( "fieldType------>" + fieldType );}EditorGUI.indentLevel--;}}EditorGUI.indentLevel--;}// 自定义绘制Inspectorif (GUILayout.Button("添加效果", GUILayout.Height(30))){if (_SelectWindow == null){_SelectWindow = new StoryEffectSelectWindow();}_SelectWindow.SetSelectHandle((string name) =>{StorySegment node = (StorySegment) target;var temp = StoryEffectFactory.CreateEffect(name);Type type = temp.GetType();node.AddStoryEffect(temp); });_SelectWindow.title = "Select StoryEffect";_SelectWindow.Show();_SelectWindow.maxSize = new Vector2(500,500);_SelectWindow.minSize = _SelectWindow.maxSize;var position = _SelectWindow.position;position.center = new Rect (0f, 0f, Screen.currentResolution.width, Screen.currentResolution.height).center;_SelectWindow.position = position;}this.serializedObject.ApplyModifiedProperties();}
上面省略了非关键代码。这样做,在允许和设置都没有问题。但是在关闭编辑器,重新进来的时候,添加的StoryEffect会直接删除掉。尝试失败。
第二种尝试
上面不能保存是数据是由于不支持的序列化类型导致的,没法保存到ScriptableObject,那好吧,只能绕个道了。创建一个数据类型,里面通过把自定义类反射获得变量属性,然后存到这个数据类中,序列化中就存类的属性值,在要使用这个值的时候动态创建自定义类型。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityScript.Macros;
using Object = System.Object;[Serializable]
public class StoryData
{[SerializeField]public string ClassName;[SerializeField]public List FiledNames = new List();[SerializeField]public List FiledTypes= new List();[SerializeField]public List FiledStringValues = new List();[SerializeField]public List FiledIntValues = new List();[SerializeField]public List FiledFloatValues = new List();[SerializeField]public List FiledBoolValues = new List();[SerializeField]public List FiledObjectValues = new List();[SerializeField]private List FiledIndexList = new List();[SerializeField]public List PrOnames= new List();[SerializeField]public List ProTypes= new List();[SerializeField]public List ProStringValues = new List();[SerializeField]public List ProIntValues = new List();[SerializeField]public List ProFloatValues = new List();[SerializeField]public List ProBoolValues = new List();[SerializeField]public List ProObjectValues = new List();[SerializeField]private List ProIndexList = new List();public void AddFiled(string name, string type, Object value){FiledNames.Add(name);FiledTypes.Add(type);if (type == "System.String"){FiledIndexList.Add(FiledNames.Count - 1);FiledIndexList.Add(FiledStringValues.Count);string v = "";if (value!= null){v = (string)value;}FiledStringValues.Add(v);}else if (type == "System.Int32" ||type == "StorySegment+StoryState"){FiledIndexList.Add(FiledNames.Count - 1);FiledIndexList.Add(FiledIntValues.Count);int v = 0;if (value != null){v = (int)value;}FiledIntValues.Add(v);}else if (type == "System.Single"){FiledIndexList.Add(FiledNames.Count - 1);FiledIndexList.Add(FiledFloatValues.Count);float v = 0;if (value != null){v = (float)value;}FiledFloatValues.Add(v);}else if (type == "System.Boolean"){FiledIndexList.Add(FiledNames.Count - 1);FiledIndexList.Add(FiledBoolValues.Count);bool v = false;if (value != null){v = (bool)value;}FiledBoolValues.Add(v);}else{FiledIndexList.Add(FiledNames.Count - 1);FiledIndexList.Add(FiledObjectValues.Count);GameObject v = null;if (value != null){v = (GameObject) value;}FiledObjectValues.Add(v);} }public void SetFiledValue(int index,Object value){string type = FiledTypes[index];int vIndex = -1;for (int i = 0; i = 0){if (type == "System.String"){string v = "";if (value!= null){v = (string)value;}FiledStringValues[vIndex] = v;}else if (type == "System.Int32" ||type == "StorySegment+StoryState"){int v = 0;if (value!= null){v = (int)value;}FiledIntValues[vIndex] = v;}else if (type == "System.Single"){float v = 0;if (value!= null){v = (float)value;}FiledFloatValues[vIndex] = v;}else if (type == "System.Boolean"){bool v = false;if (value!= null){v = (bool)value;}FiledBoolValues[vIndex] = v;}else{UnityEngine.Object v = null;if (value!= null){v = (UnityEngine.Object)value;}FiledObjectValues[vIndex] = v;} }}public object GetFiledValue(int index){string type = FiledTypes[index];int vIndex = -1;for (int i = 0; i = 0){if (type == "System.String"){return FiledStringValues[vIndex] ;}else if (type == "System.Int32" ||type == "StorySegment+StoryState"){return FiledIntValues[vIndex];}else if (type == "System.Single"){return FiledFloatValues[vIndex] ;}else if (type == "System.Boolean"){return FiledBoolValues[vIndex];}else{return FiledObjectValues[vIndex];} }return null;}public void AddPro(string name, string type, Object value){ProNames.Add(name);ProTypes.Add(type);if (type == "System.String"){ProIndexList.Add(ProNames.Count - 1);ProIndexList.Add(ProStringValues.Count);string v = "";if (value!= null){v = (string)value;}ProStringValues.Add(v);}else if (type == "System.Int32" ||type == "StorySegment+StoryState"){ProIndexList.Add(ProNames.Count - 1);ProIndexList.Add(ProIntValues.Count);int v = 0;if (value != null){v = (int)value;}ProIntValues.Add(v);}else if (type == "System.Single"){ProIndexList.Add(ProNames.Count - 1);ProIndexList.Add(ProFloatValues.Count);float v = 0;if (value != null){v = (float)value;}ProFloatValues.Add(v);}else if (type == "System.Boolean"){ProIndexList.Add(ProNames.Count - 1);ProIndexList.Add(ProBoolValues.Count);bool v = false;if (value != null){v = (bool)value;}ProBoolValues.Add(v);}else{ProIndexList.Add(ProNames.Count - 1);ProIndexList.Add(ProObjectValues.Count);UnityEngine.Object v = null;if (value != null){v = (UnityEngine.Object) value;}ProObjectValues.Add(v);} }public void SetProValue(int index,Object value){string type = ProTypes[index];int vIndex = -1;for (int i = 0; i = 0){if (type == "System.String"){string v = "";if (value!= null){v = (string)value;}ProStringValues[vIndex] = v;}else if (type == "System.Int32" ||type == "StorySegment+StoryState"){int v = 0;if (value!= null){v = (int)value;}ProIntValues[vIndex] = v;}else if (type == "System.Single"){float v = 0;if (value!= null){v = (float)value;}ProFloatValues[vIndex] = v;}else if (type == "System.Boolean"){bool v = false;if (value!= null){v = (bool)value;}ProBoolValues[vIndex] = v;}else{UnityEngine.Object v = null;if (value!= null){v = (UnityEngine.Object)value;}ProObjectValues[vIndex] = v;} }}public object GetProValue(int index){string type = ProTypes[index];int vIndex = -1;for (int i = 0; i = 0){if (type == "System.String"){return ProStringValues[vIndex] ;}else if (type == "System.Int32" ||type == "StorySegment+StoryState"){return ProIntValues[vIndex];}else if (type == "System.Single"){return ProFloatValues[vIndex] ;}else if (type == "System.Boolean"){return ProBoolValues[vIndex];}else{return ProObjectValues[vIndex];} }return null;}}
这样就把多态类型通过反射,把变量值保存在里面
public class StorySegment : ScriptableObject,IStorySegment
{public enum StoryState{NOnE= 0,START = 1,DOING = 2,END = 3,}[SerializeField]private int _ID = 0;[SerializeField]private List _StoryEffectList = new List();
}
这样我们使用StoryData的类型作为序列化。
然后创建代码:
foreach (StoryData it in _StoryEffectList){StoryEffect effet = StoryEffectFactory.CreateEffect(it.ClassName,it);if (effet.ExecState == state && !effet.IsPlaying){doList.Add(effet);Debug.Log("_______________________________________________PlayEffect:init");yield return effet.Init();}}
然后我们自定义 StorySegment的属性面板,这样就可以变成自已想要的布局效果。
public override void OnInspectorGUI(){serializedObject.Update();// 绘制全部原有属性// base.DrawDefaultInspector();//重绘数组界面EditorGUILayout.PropertyField(this.serializedObject.FindProperty("_ID"), true);_expand = EditorGUILayout.Foldout(_expand, "StoryEffectList"+"("+_target.GetStoryEffectCount()+")");if (_expand){EditorGUI.indentLevel++;for (int i = 0; i <_target.GetStoryEffectCount(); ++i){var ttt = _target.GetStoryEffectByIndex(i);var subopen = false;if (!_subExpand.TryGetValue(i, out subopen)){subopen = false;}GUILayout.BeginHorizontal();if (GUILayout.Button("删", GUILayout.Width(20), GUILayout.Height(20))){_target.RemoveStoryEffectByIndex(i);return;}_subExpand[i] = EditorGUILayout.Foldout(subopen, ttt.ClassName);GUILayout.EndHorizontal();if (subopen){EditorGUI.indentLevel++;for(int j = 0; j {var temp = StoryEffectFactory.CreateEffect(name);StoryData data = new StoryData();data.ClassName = name;Type t = temp.GetType();FieldInfo[] fieldInfos = t.GetFields(BindingFlags.Instance|BindingFlags.Static | BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Default);//Debug.Log("fieldName------>" +fieldInfos.Length);PropertyInfo[] ps = t.GetProperties(BindingFlags.Instance|BindingFlags.Static | BindingFlags.Public|BindingFlags.DeclaredOnly);foreach (var p in ps){string pName = p.Name;string pType = p.PropertyType.ToString();object pValue = p.GetValue(temp,null);if (p.CanWrite){data.AddPro(pName,pType,pValue);}}foreach (var f in fieldInfos){//字段名称string fieldName = f.Name;//字段类型string fieldType = f.FieldType.ToString();//字段的值object fieldValue = f.GetValue(temp);if (f.GetCustomAttributes(typeof(SerializeField), true).Length > 0){data.AddFiled(fieldName,fieldType,fieldValue);}}_target.AddStoryEffect(data); });_SelectWindow.title = "Select StoryEffect";_SelectWindow.Show();_SelectWindow.maxSize = new Vector2(500,500);_SelectWindow.minSize = _SelectWindow.maxSize;var position = _SelectWindow.position;position.center = new Rect (0f, 0f, Screen.currentResolution.width, Screen.currentResolution.height).center;_SelectWindow.position = position;}if (GUILayout.Button("保存", GUILayout.Height(30))){EditorUtility.SetDirty(_target);}this.serializedObject.ApplyModifiedProperties();}
注意:EditorGUILayout.PropertyField这种显示界面是能够修改后保存,如果我们自己代码修改,或者通过自己添加的文本框等界面对序列化对象赋值是不会保存在磁盘的,在下次进来就会丢失掉。所以我加了个保存,里面:
EditorUtility.SetDirty(_target);
就对当前的ScriptableObject保存一次。这样重进游戏数据就不会因为多态丢失了。