先修复一下,错误的场景 删除不必要的钓场资产 修复into场景 update:更新meta文件,修复报错 update:修复资源 修复第一章节建造 修复建造第三章 update:删除多余内容 update:更新README.md update:还原图标 update:更新README update:更新配置
168 lines
6.6 KiB
C#
168 lines
6.6 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Reflection;
|
||
using System.Text.RegularExpressions;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using UnityEngine.Playables;
|
||
using UnityEngine.Timeline;
|
||
// using static com.fpnn.common.TaskThreadPool;
|
||
|
||
public class PrefabPlayableBindingCleaner : EditorWindow
|
||
{
|
||
|
||
[MenuItem("Assets/批量清理Prefab中Playable Binding冗余元素")]
|
||
static void MenuAssetsCheckFishLayer()
|
||
{
|
||
var path = AssetDatabase.GetAssetPath(Selection.activeObject);
|
||
// 获取所有Prefab路径
|
||
string[] prefabPaths = Directory.GetFiles(
|
||
path,
|
||
"*.prefab",
|
||
SearchOption.AllDirectories
|
||
);
|
||
// 重置统计
|
||
int _processedPrefabCount = 0;
|
||
int _cleanedBindingCount = 0;
|
||
// 禁用自动刷新(提升批量处理速度)
|
||
AssetDatabase.DisallowAutoRefresh();
|
||
|
||
try
|
||
{
|
||
foreach (string prefabPath in prefabPaths)
|
||
{
|
||
_processedPrefabCount++;
|
||
// 清理单个Prefab的Binding
|
||
_cleanedBindingCount += CleanPlayableBindingInPrefab(prefabPath);
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
// 恢复自动刷新+刷新资源库
|
||
AssetDatabase.AllowAutoRefresh();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
EditorUtility.DisplayDialog("清理完成",
|
||
$"共处理 Prefab 数量:{_processedPrefabCount}\n清理冗余 Binding 数量:{_cleanedBindingCount}",
|
||
"确定");
|
||
}
|
||
public static string GetRelativePath(string path)
|
||
{
|
||
string srcPath = path.Replace("\\", "/");
|
||
var retPath = Regex.Replace(srcPath, @"^(.*?/)Assets/", "Assets/");
|
||
return retPath;
|
||
}
|
||
[MenuItem("Assets/批量清理Prefab中Playable Binding冗余元素", true)]
|
||
private static bool MenuAssetsCheckFishLayerValidation()
|
||
{
|
||
// 设置什么情况下消失/显现(the menu item will be disabled otherwise).
|
||
var path = AssetDatabase.GetAssetPath(Selection.activeObject);
|
||
return AssetDatabase.IsValidFolder(path);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 清理单个Prefab中PlayableDirector的冗余Binding
|
||
/// </summary>
|
||
/// <param name="prefabPath">Prefab完整路径</param>
|
||
/// <returns>当前Prefab清理的Binding数量</returns>
|
||
private static int CleanPlayableBindingInPrefab(string prefabPath)
|
||
{
|
||
int cleanedCount = 0;
|
||
int cleanedSecondCount = 0;
|
||
GameObject prefabRoot = null;
|
||
try
|
||
{
|
||
prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
|
||
if (prefabRoot == null)
|
||
{
|
||
Debug.LogError($"加载Prefab失败,跳过:{prefabPath}");
|
||
return 0;
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"加载Prefab失败,跳过:{prefabPath} {e.Message}");
|
||
return 0;
|
||
}
|
||
|
||
{
|
||
// 获取Prefab中所有的PlayableDirector组件
|
||
PlayableDirector[] directors = prefabRoot.GetComponentsInChildren<PlayableDirector>(true);
|
||
foreach (PlayableDirector director in directors)
|
||
{
|
||
if (director == null || director.playableAsset == null) continue;
|
||
GameObject prefab = director.gameObject;
|
||
// 获取当前PlayableDirector绑定的TimelineAsset
|
||
TimelineAsset currentTimeline = director.playableAsset as TimelineAsset;
|
||
Dictionary<UnityEngine.Object, UnityEngine.Object> bindings = new Dictionary<UnityEngine.Object, UnityEngine.Object>();
|
||
foreach (var pb in director.playableAsset.outputs)
|
||
{
|
||
var key = pb.sourceObject;
|
||
if (key != null)
|
||
{
|
||
var value = director.GetGenericBinding(key);
|
||
if (value != null)
|
||
{
|
||
bindings.Add(key, value);
|
||
}
|
||
}
|
||
}
|
||
var directorSO = new SerializedObject(director);
|
||
var sceneBindings = directorSO.FindProperty("m_SceneBindings");
|
||
var exposedRefsArray = directorSO.FindProperty("m_ExposedReferences.m_References");
|
||
int originalBindingCount = sceneBindings.arraySize;
|
||
// 移除无用的绑定
|
||
while (sceneBindings.arraySize > 0)
|
||
{
|
||
sceneBindings.DeleteArrayElementAtIndex(0);
|
||
}
|
||
|
||
|
||
// 3. 反向遍历数组(核心:避免索引错位导致ObjectDisposedException)
|
||
for (int i = exposedRefsArray.arraySize - 1; i >= 0; i--)
|
||
{
|
||
// 获取数组当前元素
|
||
SerializedProperty refElement = exposedRefsArray.GetArrayElementAtIndex(i);
|
||
//m_ExposedReferences.m_References.Array.data[13].second
|
||
// 找到元素中的"Second"字段(对应绑定的目标对象)
|
||
SerializedProperty secondProp = refElement.FindPropertyRelative("second");
|
||
// 判断Second是否为空(None)
|
||
if (secondProp == null || secondProp.objectReferenceValue == null)
|
||
{
|
||
// 删除该无效元素
|
||
cleanedSecondCount++;
|
||
exposedRefsArray.DeleteArrayElementAtIndex(i);
|
||
}
|
||
}
|
||
|
||
directorSO.ApplyModifiedProperties();
|
||
directorSO.Update();
|
||
// 恢复拷贝的Playable资源的bindings信息
|
||
// 设置有效绑定
|
||
foreach (var kv in bindings)
|
||
{
|
||
director.SetGenericBinding(kv.Key, kv.Value);
|
||
}
|
||
int newBindingCount = bindings.Count;
|
||
cleanedCount += (originalBindingCount - newBindingCount);
|
||
}
|
||
|
||
if (cleanedCount > 0 || cleanedSecondCount > 0)
|
||
{
|
||
Debug.Log($"清理Prefab:{prefabRoot.name} 中PlayableDirector绑定 清理数:{cleanedCount} {cleanedSecondCount}");
|
||
EditorUtility.SetDirty(prefabRoot);
|
||
// 保存Prefab修改(仅当有清理操作时)
|
||
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
|
||
}
|
||
}
|
||
|
||
{
|
||
// 释放Prefab资源(必须,避免内存泄漏)
|
||
PrefabUtility.UnloadPrefabContents(prefabRoot);
|
||
}
|
||
|
||
return cleanedCount;
|
||
}
|
||
} |