网络同步验证需要定点数,但是项目中大量资产文件使用了浮点数,需要工具来统一修改为定点数
- 通过将向浮点数添加特定的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();
}
}
- 通过FR2查找引用插件,查找到所有引用此脚本的资产文件
- 读取查找到的资产文件的yaml(通过
YamlDotNet
),遍历所有的Document
,通过对比yaml的m_Script
中的GUID确定yaml中信息位置 - 通过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