制作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。

alt

窗口UI刷新时使用存储的序列化UnityColor。 alt

DataManager(存档)中设置默认数据时要存序列化Color(暂时手动设,正常应该从存档中获得)。

alt

选择颜色时修改存储的Color应使用序列化的Color。

alt

其他使用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替换成自定义的序列化字典类,运行时使用其中的字典。 alt

alt