Files
MinFt/Client/Assets/Editor/CampTool/PrefabPlayableBindingCleaner.cs
Liubing\LB 3d8d4d18f3 first commit
先修复一下,错误的场景

删除不必要的钓场资产

修复into场景

update:更新meta文件,修复报错

update:修复资源

修复第一章节建造

修复建造第三章

update:删除多余内容

update:更新README.md

update:还原图标

update:更新README

update:更新配置
2026-04-28 02:17:22 +08:00

168 lines
6.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}