Last active
September 10, 2021 21:43
-
-
Save giacomelli/2d561e29beadab641a4f8b56954f53f7 to your computer and use it in GitHub Desktop.
Using an AssetPostprocessor + EditorWindow to keep assets organized on Unity projects: http://diegogiacomelli.com.br/using-an-assetpostprocessor-editorwindow-to-keep-assets-organized-on-unity-projects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEditor; | |
using UnityEngine; | |
/// <summary> | |
/// Folder organizer editor window. | |
/// http://diegogiacomelli.com.br/using-an-assetpostprocessor-editorwindow-to-keep-assets-organized-on-unity-projects | |
/// </summary> | |
public class FolderOrganizerEditorWindow : EditorWindow | |
{ | |
FolderOrganizerSettings _settings; | |
SerializedObject _settingsSO; | |
SerializedProperty _ignoreFolders; | |
SerializedProperty _validateOnImport; | |
SerializedProperty _foldersRule; | |
[MenuItem("Window/Folder organizer")] | |
public static void ShowWindow() | |
{ | |
EditorWindow.GetWindow<FolderOrganizerEditorWindow>("Folder organizer"); | |
} | |
private void OnEnable() | |
{ | |
_settings = FolderOrganizerSettings.Instance; | |
_settingsSO = new SerializedObject(_settings); | |
_ignoreFolders = _settingsSO.FindProperty(nameof(FolderOrganizerSettings.IgnoreFolders)); | |
_validateOnImport = _settingsSO.FindProperty(nameof(FolderOrganizerSettings.ValidateOnImport)); | |
_foldersRule = _settingsSO.FindProperty(nameof(FolderOrganizerSettings.FoldersRule)); | |
} | |
private void OnGUI() | |
{ | |
EditorGUI.BeginChangeCheck(); | |
EditorGUILayout.PropertyField(_ignoreFolders, true); | |
EditorGUILayout.PropertyField(_validateOnImport); | |
EditorGUILayout.PropertyField(_foldersRule, true); | |
if (GUILayout.Button("Validate")) | |
{ | |
FolderOrganizerUtility.Validate(AssetDatabase.GetAllAssetPaths(), true); | |
} | |
if (EditorGUI.EndChangeCheck()) | |
_settingsSO.ApplyModifiedProperties(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using UnityEditor; | |
/// <summary> | |
/// Folder organizer post processor. | |
/// http://diegogiacomelli.com.br/using-an-assetpostprocessor-editorwindow-to-keep-assets-organized-on-unity-projects | |
/// </summary> | |
public class FolderOrganizerPostProcessor : AssetPostprocessor | |
{ | |
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) | |
{ | |
if (FolderOrganizerSettings.Instance.ValidateOnImport) | |
{ | |
var assets = new List<string>(); | |
assets.AddRange(importedAssets); | |
assets.AddRange(movedAssets); | |
FolderOrganizerUtility.Validate(assets); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
using System.Text.RegularExpressions; | |
using UnityEditor; | |
using UnityEngine; | |
/// <summary> | |
/// Folder organizer settings. | |
/// http://diegogiacomelli.com.br/using-an-assetpostprocessor-editorwindow-to-keep-assets-organized-on-unity-projects | |
/// </summary> | |
public class FolderOrganizerSettings : ScriptableObject | |
{ | |
[Tooltip("Validation rules for folders organization")] | |
public FolderRule[] FoldersRule; | |
[Tooltip("Should run validations when an asset is imported")] | |
public bool ValidateOnImport = true; | |
[Tooltip("Folders ignored by the Folder organizer")] | |
public string[] IgnoreFolders = new string[] { "Assets/Standard Assets", "Packages" }; | |
static FolderOrganizerSettings _instance; | |
public static FolderOrganizerSettings Instance => _instance ?? (_instance = LoadAsset()); | |
private static FolderOrganizerSettings LoadAsset() | |
{ | |
var path = GetAssetPath(); | |
var asset = AssetDatabase.LoadAssetAtPath<FolderOrganizerSettings>(path); | |
if (asset == null) | |
{ | |
asset = CreateInstance<FolderOrganizerSettings>(); | |
AssetDatabase.CreateAsset(asset, path); | |
AssetDatabase.SaveAssets(); | |
} | |
return asset; | |
} | |
private static string GetAssetPath([CallerFilePath] string callerFilePath = null) | |
{ | |
var folder = Path.GetDirectoryName(callerFilePath); | |
folder = folder.Substring(folder.LastIndexOf("/Assets/", StringComparison.Ordinal) + 1); | |
return Path.Combine(folder, "FolderOrganizerSettings.asset"); | |
} | |
} | |
/// <summary> | |
/// Folder rule. | |
/// http://diegogiacomelli.com.br/using-an-assetpostprocessor-editorwindow-to-keep-assets-organized-on-unity-projects | |
/// </summary> | |
[Serializable] | |
public class FolderRule | |
{ | |
[Tooltip("Regular expression pattern to find assets by path, e.g., '.+\\.png'")] | |
public string AssetFindPattern; | |
[Tooltip("Expected folders for assets found by the Asset Find Pattern, e.g., 'Assets/Sprites'")] | |
public string[] ExpectedFolders = new string[1]; | |
public bool Validate(string assetPath) | |
{ | |
if (Regex.IsMatch(assetPath, AssetFindPattern)) | |
{ | |
var currentAssetFolder = Path.GetDirectoryName(assetPath); | |
if (ExpectedFolders.All(expected => expected != currentAssetFolder)) | |
{ | |
var assetName = Path.GetFileName(assetPath); | |
Debug.LogWarning($"Asset <b>{assetName}</b> is in folder <b><color=red>{currentAssetFolder}</color></b>, but should be moved to folder <b><color=green>{String.Join(" or ", ExpectedFolders)}</color></b>."); | |
return false; | |
} | |
} | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEditor; | |
using UnityEngine; | |
/// <summary> | |
/// Folder organizer utility. | |
/// http://diegogiacomelli.com.br/using-an-assetpostprocessor-editorwindow-to-keep-assets-organized-on-unity-projects | |
/// </summary> | |
public static class FolderOrganizerUtility | |
{ | |
public static void Validate(IEnumerable<string> assets, bool interactable = false) | |
{ | |
if (interactable) | |
Debug.Log("Folder organizer validation started."); | |
var settings = FolderOrganizerSettings.Instance; | |
var ignoreFolders = settings.IgnoreFolders; | |
var folderRules = settings.FoldersRule; | |
var assetsCount = assets.Count(); | |
float step = 0; | |
var validAssetsCount = 0; | |
var invalidAssetsCount = 0; | |
foreach (string assetPath in assets) | |
{ | |
step++; | |
if (interactable && EditorUtility.DisplayCancelableProgressBar("Folder organizer", "Validating assets...", step / assetsCount)) | |
break; | |
// If asset path starts with any ignore folders. | |
if (ignoreFolders.Any(assetPath.StartsWith)) | |
continue; | |
foreach (var folderRule in folderRules) | |
{ | |
if (folderRule.Validate(assetPath)) | |
validAssetsCount++; | |
else | |
invalidAssetsCount++; | |
} | |
} | |
if (interactable) | |
{ | |
EditorUtility.ClearProgressBar(); | |
var msg = invalidAssetsCount > 0 | |
? $"{validAssetsCount} are in the right folder and {invalidAssetsCount} are in the wrong folder.\nMore details on Console Window." | |
: "All assets are valid."; | |
Debug.Log(msg); | |
EditorUtility.DisplayDialog("Folder organizer", msg, "OK"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment