Files
5.24BuYu/Assets/MyGame/Editor/StripVisualPrefabTool.cs
T
2026-10-20 18:30:03 +08:00

116 lines
4.9 KiB
C#
Raw 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 UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Reflection;
/// <summary>
/// 纯视觉 Prefab 生成器:剥掉所有逻辑脚本,只保留表现组件。
/// 用法:Project 窗口选中一个/多个 prefab → Tools → 生成纯视觉Prefab
/// 输出:同目录 &lt;原名&gt;_Visual.prefab(不覆盖原文件)
///
/// 保留白名单:Unity 内置表现组件、UGUI、TMP、Spine、Animancer、DOTween、URP Volume
/// 删除:其余所有 MonoBehaviour(BY_* 游戏逻辑、LuaBehaviour、FMOD、NGUI 等)
/// Missing 脚本(原逻辑脚本移除后的残留)同时清理。
/// </summary>
public static class StripVisualPrefabTool
{
// 表现类程序集/命名空间白名单
static readonly string[] KeepNamespaces = {
"UnityEngine.UI", "TMPro", "Spine", "Spine.Unity", "Animancer",
"DG.Tweening", "UnityEngine.Rendering", "UnityEngine.U2D",
"UnityEngine", "UnityEngine.Experimental",
};
// 表现类程序集名(.dll)
static readonly string[] KeepAssemblies = {
"UnityEngine.UI", "Unity.TextMeshPro", "Unity.Timeline",
"Spine", "Spine.Unity", "Animancer", "DOTween", "DOTweenPro",
"Unity.RenderPipelines.Core.Runtime", "Unity.RenderPipelines.Universal.Runtime",
"Unity.2D.Animation.Runtime", "Unity.Animator2D",
"Assembly-CSharp", // 视觉辅助:UIGradient/TextSpacing/Outline8/Z1_EffectFollow 等
};
[MenuItem("Tools/生成纯视觉Prefab(剥离逻辑脚本)")]
public static void StripSelected()
{
// 收集目标:选中的 prefab + 选中文件夹下递归的所有 prefab
var prefabPaths = new HashSet<string>();
foreach (var obj in Selection.objects)
{
var path = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(path)) continue;
if (path.EndsWith(".prefab")) prefabPaths.Add(path);
else if (System.IO.Directory.Exists(path))
{
foreach (var f in System.IO.Directory.GetFiles(path, "*.prefab", System.IO.SearchOption.AllDirectories))
prefabPaths.Add(f.Replace('\\', '/'));
}
}
if (prefabPaths.Count == 0) { Debug.LogError("[纯视觉] 未找到 prefab——请选中 prefab 或包含 prefab 的文件夹"); return; }
Debug.Log($"[纯视觉] 共 {prefabPaths.Count} 个 prefab 待处理...");
int done = 0;
foreach (var path in prefabPaths) { StripOne(path); done++; }
Debug.Log($"[纯视觉] 处理完成 {done} 个 prefab(输出为 *_Visual.prefab)");
}
[MenuItem("Tools/生成纯视觉Prefab(剥离逻辑脚本)", true)]
static bool Validate() { return Selection.objects != null && Selection.objects.Length > 0; }
static void StripOne(string path)
{
var root = PrefabUtility.LoadPrefabContents(path);
int removed = 0;
try
{
// 1. 删除非白名单 MonoBehaviour
var mbs = root.GetComponentsInChildren<MonoBehaviour>(true);
var toRemove = new List<Component>();
foreach (var mb in mbs)
{
if (mb == null) continue;
if (!IsVisual(mb)) toRemove.Add(mb);
}
foreach (var c in toRemove) { Object.DestroyImmediate(c, true); removed++; }
// 2. 清理 Missing 脚本组件(serialized 里的 m_Script 已失效的)
foreach (var t in root.GetComponentsInChildren<Transform>(true))
RemoveMissingScripts(t.gameObject);
// 3. 另存
string dir = System.IO.Path.GetDirectoryName(path).Replace('\\', '/');
string name = System.IO.Path.GetFileNameWithoutExtension(path);
string newPath = $"{dir}/{name}_Visual.prefab";
PrefabUtility.SaveAsPrefabAsset(root, newPath);
Debug.Log($"[纯视觉] {name} → {newPath} (剥离 {removed} 个逻辑组件)");
}
finally { PrefabUtility.UnloadPrefabContents(root); }
}
static bool IsVisual(Component c)
{
var t = c.GetType();
// 程序集白名单(精确优先)
var asm = t.Assembly.GetName().Name;
foreach (var k in KeepAssemblies)
if (asm == k) return true;
// 命名空间白名单
var ns = t.Namespace ?? "";
foreach (var k in KeepNamespaces)
if (ns == k || ns.StartsWith(k + ".")) return true;
return false;
}
static void RemoveMissingScripts(GameObject go)
{
var so = new SerializedObject(go);
var comps = so.FindProperty("m_Component");
for (int i = comps.arraySize - 1; i >= 0; i--)
{
var comp = comps.GetArrayElementAtIndex(i).FindPropertyRelative("component");
if (comp != null && comp.objectReferenceValue == null)
comps.DeleteArrayElementAtIndex(i);
}
so.ApplyModifiedPropertiesWithoutUndo();
}
}