Unity资产文件浮点数修改为定点数工具记录


网络同步验证需要定点数,但是项目中大量资产文件使用了浮点数,需要工具来统一修改为定点数

  1. 通过将向浮点数添加特定的Attribute,并将类型替换成定点数类型,在OnPostprocessAllAssets时记录修改的cs文件GUID
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets,
        string[] movedFromAssetPaths) {
        _lastProcessedScriptTypes.Clear();
        _tsSerRecord = (TSSerScriptRecord)EditorGUIUtility.Load(TSSerRecordRelativePath);
        bool isDirty = false;
        foreach (string path in importedAssets) {
            Debug.Log("reimport Asset: " + path);
            if (Path.GetExtension(path) == ".cs") {
                string guid = AssetDatabase.AssetPathToGUID(path);
                var monoScript = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
                if (monoScript != null) {
                    var type = monoScript.GetClass();
                    if (type != null) {
                        Debug.Log($"monoScript type: {type}");
                        if (type.IsSubclassOf(typeof(Object))) {
                            var tsFields =
                                (from field in type.GetFields(BindingFlags.Instance | BindingFlags.Static |
                                                              BindingFlags.Public | BindingFlags.NonPublic)
                                    where field.IsDefined(typeof(TSSerAttribute), false)
                                    select field).ToArray();
                            if (tsFields.Length <= 0) continue;
                            if (_tsSerRecord.changedScriptGuids.Contains(guid)) continue;
                            _tsSerRecord.changedScriptGuids.Add(guid);
                            isDirty = true;
                        }
                    }
                }
            }
        }

        if (isDirty) {
            EditorUtility.SetDirty(_tsSerRecord);
            AssetDatabase.Refresh();
        }
    }
  1. 通过FR2查找引用插件,查找到所有引用此脚本的资产文件
  2. 读取查找到的资产文件的yaml(通过YamlDotNet),遍历所有的Document,通过对比yaml的m_Script中的GUID确定yaml中信息位置
  3. 通过yaml可以得到修改前的旧值通过SerializedObject查找SerializedProperty设置转换后的定点值,最后应用修改ApplyModifiedProperties
static void ProcessTsAttributeSO(List<FieldInfo> fieldList, string scriptGuid, ScriptableObject so)
{
    var path = AssetDatabase.GetAssetPath(so);
    path = Path.Combine(Application.dataPath.Replace("/Assets", ""), path);
    var yaml = LoadYamlStream(path);
    if (yaml == null)
    {
        Debug.LogError($"LoadYamlStream Error {path}");
        return;
    }
    for (int i = 0; i < yaml.Documents.Count; i++)
    {
        var mapping = (YamlMappingNode)yaml.Documents[i].RootNode;
        foreach (var entry in mapping.Children)
        {
            if (entry.Key.ToString().Contains("MonoBehaviour"))
            {
                if (entry.Value is YamlMappingNode monoMapping)
                {
                    string guid =
                        ((monoMapping.Children["m_Script"] as YamlMappingNode)?.Children["guid"] as
                            YamlScalarNode)?.Value;
                    if (guid == scriptGuid)
                    {
                        var serObj = new SerializedObject(so);
                        bool isDirty = false;
                        foreach (var field in fieldList)
                        {
                            var uncovertNode = monoMapping.Children[field.Name];
                            isDirty = SingleValueConvertTsValue(serObj, field, uncovertNode);
                        }

                        if (isDirty)
                        {
                            serObj.ApplyModifiedProperties();
                            EditorUtility.SetDirty(so);
                        }
                    }
                }
            }
        }
    }
}
    static bool SingleValueConvertTsValue(SerializedObject serObj, FieldInfo fi, YamlNode uncovertNode)
    {
        var fieldType = fi.FieldType;

        #region FP
        if (fieldType == typeof(FP))
        {
            var targetNode = uncovertNode as YamlScalarNode;
            if (targetNode != null)
            {
                if (float.TryParse(targetNode.Value,
    out var oldValue))
                {
                    serObj.FindProperty(fi.Name)
                            .FindPropertyRelative("_serializedValue")
                            .longValue =
                        ((FP)oldValue).RawValue;
                    return true;
                }
            }
        }

        #endregion FP end

        #region TSVector2
        if (fieldType == typeof(TSVector2))
        {
            var targetNode = uncovertNode as YamlMappingNode;
            if(targetNode != null)
            {
                bool boolX, boolY;
                var strX = (targetNode.Children["x"] as YamlScalarNode)?.Value;
                var strY = (targetNode.Children["y"] as YamlScalarNode)?.Value;
                if(boolX = float.TryParse(strX, out var oldFloatX))
                {
                    serObj.FindProperty(fi.Name)
                        .FindPropertyRelative("x")
                        .FindPropertyRelative("_serializedValue").longValue = ((FP)oldFloatX).RawValue;
                }

                if (boolY = float.TryParse(strY, out var oldFloatY))
                {
                    serObj.FindProperty(fi.Name)
                        .FindPropertyRelative("y")
                        .FindPropertyRelative("_serializedValue").longValue = ((FP)oldFloatY).RawValue;
                }
                return boolX && boolY;
            }
        }
        #endregion TSVector2 end

        #region TSVector
        if (fieldType == typeof(TSVector))
        {
            var targetNode = uncovertNode as YamlMappingNode;
            if (targetNode != null)
            {
                bool boolX, boolY, boolZ;
                var strX = (targetNode.Children["x"] as YamlScalarNode)?.Value;
                var strY = (targetNode.Children["y"] as YamlScalarNode)?.Value;
                var strZ = (targetNode.Children["z"] as YamlScalarNode)?.Value;
                if (boolX = float.TryParse(strX, out var oldFloatX))
                {
                    serObj.FindProperty(fi.Name)
                        .FindPropertyRelative("x")
                        .FindPropertyRelative("_serializedValue").longValue = ((FP)oldFloatX).RawValue;
                }

                if (boolY = float.TryParse(strY, out var oldFloatY))
                {
                    serObj.FindProperty(fi.Name)
                        .FindPropertyRelative("y")
                        .FindPropertyRelative("_serializedValue").longValue = ((FP)oldFloatY).RawValue;
                }

                if (boolZ = float.TryParse(strZ, out var oldFloatZ))
                {
                    serObj.FindProperty(fi.Name)
                        .FindPropertyRelative("z")
                        .FindPropertyRelative("_serializedValue").longValue = ((FP)oldFloatZ).RawValue;
                }
                return boolX && boolY && boolZ;
            }
        }

        #endregion TSVector2 end

        #region Curve

        if (fieldType == typeof(Curve))
        {
            var targetNode = uncovertNode as YamlMappingNode;
            if (targetNode != null)
            {
                // 空曲线可能会出现空情况
                if (targetNode.Children.ContainsKey("m_Curve"))
                {
                    var curveSeqNode = targetNode.Children["m_Curve"] as YamlSequenceNode;
                    if (curveSeqNode != null)
                    {
                        var curvekeyListSerProperty = serObj.FindProperty(fi.Name)
                            .FindPropertyRelative("keys")
                            .FindPropertyRelative("innerlist");
                        curvekeyListSerProperty.ClearArray();
                        bool errorState = true;
                        for (int i = 0; i < curveSeqNode.Children.Count; i++)
                        {
                            var mapNode = curveSeqNode.Children[i] as YamlMappingNode;
                            if (mapNode != null)
                            {
                                bool boolTime, boolValue, boolInTangent, boolOutTangent;

                                var strTime = (mapNode.Children["time"] as YamlScalarNode)?.Value;
                                var strValue = (mapNode.Children["value"] as YamlScalarNode)?.Value;
                                var strInTangent = (mapNode.Children["inSlope"] as YamlScalarNode)?.Value;
                                var strOutTangent = (mapNode.Children["outSlope"] as YamlScalarNode)?.Value;
								
								这个是通过SerializedProperty修改List类型
                                curvekeyListSerProperty.InsertArrayElementAtIndex(i);
                                var insertElement = curvekeyListSerProperty.GetArrayElementAtIndex(i);

                                if (boolTime = float.TryParse(strTime, out var floatTime))
                                {
                                    insertElement
                                        .FindPropertyRelative("position")
                                        .FindPropertyRelative("_serializedValue")
                                        .longValue = ((FP)floatTime).RawValue;
                                }
                                if (boolValue = float.TryParse(strValue, out var floatValue))
                                {
                                    insertElement
                                        .FindPropertyRelative("value")
                                        .FindPropertyRelative("_serializedValue")
                                        .longValue = ((FP)floatValue).RawValue;
                                }
                                if (boolInTangent = float.TryParse(strInTangent, out var floatInTangent))
                                {
                                    insertElement
                                        .FindPropertyRelative("tangentIn")
                                        .FindPropertyRelative("_serializedValue")
                                        .longValue = ((FP)floatInTangent).RawValue;
                                }
                                if (boolOutTangent = float.TryParse(strOutTangent, out var floatOutTangent))
                                {
                                    insertElement
                                        .FindPropertyRelative("tangentOut")
                                        .FindPropertyRelative("_serializedValue")
                                        .longValue = ((FP)floatOutTangent).RawValue;
                                }

                                errorState = errorState && boolTime && boolValue && boolInTangent && boolOutTangent;
                            }
                        }
                        return errorState;
                    }
                }
            }
        }


注意 当在MonoBehaviour类型cs脚本中修改值时,资产文件yaml其实是不变的,但是如果你这时候修改Inspector,yaml将会被刷新成新信息,你就无法获得以前的旧值了,上面的核心思路就是找到引用资产文件,通过yaml获得旧值,通过SerializedObject设置新值和刷新yaml


上一篇:JavaScript AJAX实例


下一篇:如何使用GitHub?