// Assets/Editor/CleanExportSettings.cs // ScriptableObject + Custom Editor for configuring the exporter. // Open via: Tools ? Assets Exporter ? Open Clean Export Settings using UnityEngine; using UnityEditor; public class CleanExportSettings : ScriptableObject { // ---- Destination root ---- [Header("Destination")] [Tooltip("Root folder (under 'Assets/') where exported assets will be saved.")] public string rootFolder = "Assets/_Client_Assets"; // ---- Missing scripts ---- [Header("Missing Scripts")] [Tooltip("If enabled, removes 'Missing (MonoBehaviour)' components during cleanup.")] public bool removeMissingScripts = true; // ---- Whitelist (keep these components) ---- [Header("Keep these components")] public bool meshFilter = true; public bool meshRenderer = true; public bool skinnedMesh = true; [Space] public bool meshCollider = true; public bool boxCollider = true; public bool sphereCollider = true; public bool capsuleCollider = true; [Space] public bool terrain = true; public bool terrainCollider = true; // Common optional components [Header("Common optional components")] public bool camera = false; public bool rigidbody = false; public bool animator = false; public bool light = false; public bool audioSource = false; public bool lodGroup = false; public bool rectTransform = false; public bool trailRenderer = false; public bool lineRenderer = false; public bool spriteRenderer = false; public bool reflectionProbe = false; public bool characterController= false; [Header("Extra component type names (fully qualified)")] [Tooltip("Fully-qualified type names to also keep.\nExample: 'UnityEngine.RectTransform, UnityEngine'")] public string[] extraAllowedTypes = new string[0]; // ---- PNG Texture Import Settings ---- [Header("PNG Texture Import Settings")] [Tooltip("Bake every texture to PNG even if it is a regular asset (usually OFF).")] public bool forceBakeAllTextures = false; [Tooltip("Generate mipmaps for baked/copied PNGs.")] public bool pngMipmap = false; [Tooltip("Max size for baked/copied PNGs (e.g., 256/512/1024/2048/4096/8192).")] public int pngMaxSize = 1024; [Tooltip("Compression mode for PNGs.")] public TextureImporterCompression pngCompression = TextureImporterCompression.CompressedHQ; [Tooltip("Use Crunch compression for PNGs (smaller files, slower import).")] public bool pngUseCrunch = false; [Range(0, 100)] [Tooltip("Crunch compression quality (0-100).")] public int pngCrunchQuality = 50; // ---- Scene options ---- [Header("Scene options")] [Tooltip("If enabled, replaces the original objects in the active scene with the exported prefabs.")] public bool replaceInScene = false; [Tooltip("If enabled, creates a new scene with the exported prefabs instantiated and saves it.")] public bool createNewScene = false; [Tooltip("Folder (under 'Assets/') where the new .unity scene will be saved.")] public string newSceneFolder = "Assets/_Client_Assets/Scenes"; [Tooltip("Prefix for the new scene name. The final name will be 'Scene_yyyyMMdd_HHmmss.unity'.")] public string newSceneNamePrefix = "Exported"; // ---- Loader/Creator ---- public const string DefaultPath = "Assets/_Client_Assets/CleanExportSettings.asset"; public static CleanExportSettings LoadOrCreateAtDefaultPath() { var settings = AssetDatabase.LoadAssetAtPath(DefaultPath); if (settings != null) return settings; var dir = System.IO.Path.GetDirectoryName(DefaultPath).Replace("\\", "/"); if (!AssetDatabase.IsValidFolder(dir)) { var parent = System.IO.Path.GetDirectoryName(dir)?.Replace("\\", "/") ?? "Assets"; var leaf = System.IO.Path.GetFileName(dir); if (!AssetDatabase.IsValidFolder(parent)) AssetDatabase.CreateFolder("Assets", "_Client_Assets"); AssetDatabase.CreateFolder(parent, leaf); } settings = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(settings, DefaultPath); AssetDatabase.SaveAssets(); return settings; } } // -------- Custom Inspector (nice UI + folder picker) -------- [CustomEditor(typeof(CleanExportSettings))] public class CleanExportSettingsEditor : Editor { public override void OnInspectorGUI() { var s = (CleanExportSettings)target; EditorGUI.BeginChangeCheck(); // Destination root folder with picker EditorGUILayout.LabelField("Destination", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); s.rootFolder = EditorGUILayout.TextField("Root Folder", s.rootFolder); if (GUILayout.Button("Pick…", GUILayout.Width(60))) { string start = Application.dataPath; var picked = EditorUtility.OpenFolderPanel("Select Root Folder (inside Assets)", start, ""); if (!string.IsNullOrEmpty(picked)) { picked = picked.Replace("\\", "/"); string data = Application.dataPath.Replace("\\", "/"); if (picked.StartsWith(data)) { s.rootFolder = "Assets" + picked.Substring(data.Length); } else { EditorUtility.DisplayDialog("Invalid folder", "Please choose a folder *inside* your project's Assets/.", "OK"); } } } if (GUILayout.Button("Open", GUILayout.Width(60))) { var path = s.rootFolder; if (!string.IsNullOrWhiteSpace(path) && AssetDatabase.IsValidFolder(path)) { var obj = AssetDatabase.LoadAssetAtPath(path); Selection.activeObject = obj; EditorGUIUtility.PingObject(obj); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); // Missing scripts EditorGUILayout.LabelField("Missing Scripts", EditorStyles.boldLabel); s.removeMissingScripts = EditorGUILayout.Toggle("Remove missing scripts", s.removeMissingScripts); EditorGUILayout.Space(); // Keep components EditorGUILayout.LabelField("Keep these components", EditorStyles.boldLabel); EditorGUILayout.BeginVertical("box"); s.meshFilter = EditorGUILayout.Toggle("Mesh Filter", s.meshFilter); s.meshRenderer = EditorGUILayout.Toggle("Mesh Renderer", s.meshRenderer); s.skinnedMesh = EditorGUILayout.Toggle("Skinned Mesh Renderer", s.skinnedMesh); EditorGUILayout.Space(4); s.meshCollider = EditorGUILayout.Toggle("Mesh Collider", s.meshCollider); s.boxCollider = EditorGUILayout.Toggle("Box Collider", s.boxCollider); s.sphereCollider = EditorGUILayout.Toggle("Sphere Collider", s.sphereCollider); s.capsuleCollider = EditorGUILayout.Toggle("Capsule Collider", s.capsuleCollider); EditorGUILayout.Space(4); s.terrain = EditorGUILayout.Toggle("Terrain", s.terrain); s.terrainCollider = EditorGUILayout.Toggle("Terrain Collider", s.terrainCollider); EditorGUILayout.EndVertical(); // Common optional components EditorGUILayout.LabelField("Common optional components", EditorStyles.boldLabel); EditorGUILayout.BeginVertical("box"); s.camera = EditorGUILayout.Toggle("Camera", s.camera); s.rigidbody = EditorGUILayout.Toggle("Rigidbody", s.rigidbody); s.animator = EditorGUILayout.Toggle("Animator", s.animator); s.light = EditorGUILayout.Toggle("Light", s.light); s.audioSource = EditorGUILayout.Toggle("Audio Source", s.audioSource); s.lodGroup = EditorGUILayout.Toggle("LOD Group", s.lodGroup); s.rectTransform = EditorGUILayout.Toggle("Rect Transform", s.rectTransform); s.trailRenderer = EditorGUILayout.Toggle("Trail Renderer", s.trailRenderer); s.lineRenderer = EditorGUILayout.Toggle("Line Renderer", s.lineRenderer); s.spriteRenderer = EditorGUILayout.Toggle("Sprite Renderer", s.spriteRenderer); s.reflectionProbe = EditorGUILayout.Toggle("Reflection Probe", s.reflectionProbe); s.characterController= EditorGUILayout.Toggle("Character Controller", s.characterController); EditorGUILayout.EndVertical(); // Extra type names EditorGUILayout.LabelField("Extra component type names (fully qualified)", EditorStyles.boldLabel); SerializedProperty extras = serializedObject.FindProperty("extraAllowedTypes"); EditorGUILayout.PropertyField(extras, true); EditorGUILayout.Space(); // PNG settings EditorGUILayout.LabelField("PNG Texture Import Settings", EditorStyles.boldLabel); s.forceBakeAllTextures = EditorGUILayout.Toggle("Force bake all textures", s.forceBakeAllTextures); s.pngMipmap = EditorGUILayout.Toggle("Generate mipmaps", s.pngMipmap); s.pngMaxSize = EditorGUILayout.IntPopup("Max size", s.pngMaxSize, new[] { "256","512","1024","2048","4096","8192" }, new[] { 256,512,1024,2048,4096,8192 }); s.pngCompression = (TextureImporterCompression)EditorGUILayout.EnumPopup("Compression", s.pngCompression); s.pngUseCrunch = EditorGUILayout.Toggle("Use Crunch", s.pngUseCrunch); s.pngCrunchQuality = EditorGUILayout.IntSlider("Crunch quality", s.pngCrunchQuality, 0, 100); EditorGUILayout.Space(); // Scene options EditorGUILayout.LabelField("Scene options", EditorStyles.boldLabel); s.replaceInScene = EditorGUILayout.Toggle("Replace in current scene", s.replaceInScene); s.createNewScene = EditorGUILayout.Toggle("Create new scene", s.createNewScene); EditorGUILayout.BeginHorizontal(); s.newSceneFolder = EditorGUILayout.TextField("New scene folder", s.newSceneFolder); if (GUILayout.Button("Pick…", GUILayout.Width(60))) { string start = Application.dataPath; var picked = EditorUtility.OpenFolderPanel("Select Scene Folder (inside Assets)", start, ""); if (!string.IsNullOrEmpty(picked)) { picked = picked.Replace("\\", "/"); string data = Application.dataPath.Replace("\\", "/"); if (picked.StartsWith(data)) { s.newSceneFolder = "Assets" + picked.Substring(data.Length); } else { EditorUtility.DisplayDialog("Invalid folder", "Please choose a folder *inside* your project's Assets/.", "OK"); } } } if (GUILayout.Button("Open", GUILayout.Width(60))) { var path = s.newSceneFolder; if (!string.IsNullOrWhiteSpace(path) && AssetDatabase.IsValidFolder(path)) { var obj = AssetDatabase.LoadAssetAtPath(path); Selection.activeObject = obj; EditorGUIUtility.PingObject(obj); } } EditorGUILayout.EndHorizontal(); s.newSceneNamePrefix = EditorGUILayout.TextField("New scene name prefix", s.newSceneNamePrefix); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); EditorUtility.SetDirty(s); } } }