制作Color/Vector/Dictionary类型的存档
Color类型存档
创建序列化的Color类
其实所谓的序列化Color就是把我们想要的数据单独封装成结构体进行存储,舍弃掉Unity数据类型中自带的额外方法和属性。
using UnityEngine;
using System;
/// <summary>
/// 可序列化的颜色
/// </summary>
[Serializable]
public struct Serialization_Color
{
public float r, g, b, a;
public Serialization_Color(float r, float g, float b, float a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public override string ToString()
{
return $"({r},{g},{b},{a})";
}
public override int GetHashCode()
{
return this.ConverToUnityColor().GetHashCode();
}
}
这里重载GetHashCode是为了将来序列化Color做字典Key查找时避免去调用结构体基类Object里的GetHasCode造成装箱性能,借助Color的HashCode。
创建Color类型转换的拓展方法
我们希望通过Color.的方式直接进行两种类型的转换,故使用拓展方法。
public static class Serialization_ColorExtensions
{
public static Color ConverToUnityColor(this Serialization_Color color)
{
return new Color(color.r, color.g, color.b, color.a);
}
public static Serialization_Color ConverToSerializatioColor(this Color color)
{
return new Serialization_Color(color.r, color.g, color.b, color.a);
}
}
修改CustomCharacterPartData中的Color类型
CustomCharacterData类存着当前角色的部位数据,里有CustomPartDataDic字典,索引是face,hair,cloth,value装着每个部位的数据类CustomCharacterPartData,数据类里有每个部位的类型索引,颜色),用于与存档对接,为方便起见,在CustomCharacterData里就存成序列化的Color类型。
/// <summary>
/// 自定义角色部位的数据
/// </summary>
[Serializable]
public class CustomCharacterPartData
{
public int Index; //某个部位第几种类型
public float Size;
public float Height;
public Serialization_Color Color1;
public Serialization_Color Color2;
}
对应修改序列化和Unity Color的代码
原先颜色数据进行修改和保存的地方对应修改。
Player_View中需要颜色数据设置模型颜色,将存储的序列化Color转换成序列化Unity Color。
窗口UI刷新时使用存储的序列化UnityColor。
DataManager(存档)中设置默认数据时要存序列化Color(暂时手动设,正常应该从存档中获得)。
选择颜色时修改存储的Color应使用序列化的Color。
其他使用Color的地方不涉及类型转换固不用更改,出现需要用或修改CustomCharacterData存储的数据时要做转换。
当然也可以做运算符重载添加隐式转换的逻辑,这样无论是UnityColor还是序列化的Color都不用显示转换了,但为了逻辑清晰不推荐。
public static implicit operator Serialization_Color (Color color)
{
return new Serialization_Color(color.r, color.g, color.b, color.a);
}
Vector类型的存档
与Color保存r,g,b类似,Vector保存坐标x,y,z,这里实现Vector3(int),Vector2(int)。
using UnityEngine;
using System;
/// <summary>
/// 可序列化的Vector3
/// </summary>
[Serializable]
public struct Serialization_Vector3
{
public float x, y, z;
public Serialization_Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public override string ToString()
{
return $"({x},{y},{z})";
}
public override int GetHashCode()
{
return this.ConverToUnityVector3().GetHashCode();
}
}
[Serializable]
public struct Serialization_Vector2
{
public float x, y;
public Serialization_Vector2(float x, float y)
{
this.x = x;
this.y = y;
}
public override string ToString()
{
return $"({x},{y})";
}
public override int GetHashCode()
{
return this.ConverToUnityVector2().GetHashCode();
}
}
public static class SerializationVectorExtensions
{
#region Vector3扩展方法
public static Vector3 ConverToUnityVector3(this Serialization_Vector3 sV3)
{
return new Vector3(sV3.x, sV3.y, sV3.z);
}
public static Serialization_Vector3 ConverToSerializatioVector3(this Vector3 sV3)
{
return new Serialization_Vector3(sV3.x, sV3.y, sV3.z);
}
public static Vector3 ConverToUnityVector3Int(this Serialization_Vector3 sV3)
{
return new Vector3Int((int)sV3.x, (int)sV3.y, (int)sV3.z);
}
public static Serialization_Vector3 ConverToSerializatioVector3Int(this Vector3 sV3)
{
return new Serialization_Vector3((int)sV3.x, (int)sV3.y, (int)sV3.z);
}
#endregion
#region Vector2扩展方法
public static Vector2 ConverToUnityVector2(this Serialization_Vector2 sV2)
{
return new Vector2(sV2.x, sV2.y);
}
public static Serialization_Vector2 ConverToSerializatioVector2(this Vector2 sV2)
{
return new Serialization_Vector2(sV2.x, sV2.y);
}
public static Vector2 ConverToUnityVector2Int(this Serialization_Vector2 sV2)
{
return new Vector2Int((int)sV2.x, (int)sV2.y);
}
public static Serialization_Vector2 ConverToSerializatioVector2Int(this Vector2 sV2)
{
return new Serialization_Vector2((int)sV2.x, (int)sV2.y);
}
#endregion
}
Dictionary类型的存档
像Color和Vector的序列化核心思路是把关键的数据单独封装成一个结构体,用的时候通过自己定义的结构体.变量的方式访问,和Unity自带的数据类型用起来没有差异。
字典比较特殊,不像Color保存RGBA单个变量,而是保存Key和Value序列,key和Value之间有映射关系,必须组合使用,而Color里r,g,b之间并没有直接关系,可以单独使用。也就是说字典的序列化比Vector和Color做一步重新组合。
因此字典的序列化是把Key序列和Value序列分别封装成List(List可以进行序列化)存储,读取List数据后真要用的时候要多一步组合回一个字典。
为方便用就组合放在序列化之后直接执行了,这样外界直接用组合完的字典即可(当然也可以包装下常用的增删改查方法,直接序列化类+对应的方法即可,不用访问持有的字典),这个字典只是为了方便用,不用序列化。
相当于给字典包了一层壳,在序列化和反序列化时自动拆分成List存储、组合成Dictionary使用。
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
/// <summary>
/// 可序列化的Vector3
/// </summary>
[Serializable]
public class Serialization_Dic<K,V>
{
private List<K> keyList;
private List<V> valueList;
[NonSerialized] //不用序列化,存和读取时不用,只在运行时用
private Dictionary<K, V> dictionary;
public Dictionary<K, V> Dictionary { get => dictionary; }
public Serialization_Dic(Dictionary<K, V> dictionary)
{
this.dictionary = dictionary;
}
public Serialization_Dic()
{
this.dictionary = new Dictionary<K, V>();
}
//序列化前把字典的内容放进List
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
keyList = new List<K>(dictionary.Count);
valueList = new List<V>(dictionary.Count);
foreach (var kv in dictionary)
{
keyList.Add(kv.Key);
valueList.Add(kv.Value);
}
}
// 反序列化读取List后组装字典
[OnDeserialized]
private void OnDesrialized(StreamingContext context)
{
dictionary = new Dictionary<K, V>(keyList.Count);
for (int i = 0; i < keyList.Count; i++)
{
dictionary.Add(keyList[i], valueList[i]);
}
keyList.Clear();
valueList.Clear();
}
}
将原来的Dictionary替换成自定义的序列化字典类,运行时使用其中的字典。