using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; using System.Linq; using System; /// /// 모션 캡쳐 파일 네이밍 편의성 툴 /// - 선택한 파일들에 일괄적으로 접미사/접두사 추가 /// - 번호 매기기, 날짜 스탬프 등 다양한 네이밍 패턴 지원 /// public class MotionCaptureFileRenamer : EditorWindow { #region Nested Classes [Serializable] public class NamingPreset { public string presetName = "New Preset"; public NamingMode mode = NamingMode.Suffix; public string customText = ""; public int startNumber = 1; public int numberPadding = 3; public string separator = "_"; } public enum NamingMode { Suffix, // 파일명 뒤에 추가 Prefix, // 파일명 앞에 추가 Replace, // 전체 이름 변경 Sequential // 순차 번호 } #endregion #region Fields private List selectedFiles = new List(); private Vector2 scrollPosition; private Vector2 fileListScrollPosition; private Vector2 previewScrollPosition; // Naming settings private NamingMode namingMode = NamingMode.Suffix; private string customText = "_ok"; private int startNumber = 1; private int numberPadding = 3; private string separator = "_"; // Presets private List presets = new List(); private int selectedPresetIndex = -1; private bool showPresetSection = false; private string newPresetName = "My Preset"; // UI Style private GUIStyle headerStyle; private GUIStyle boxStyle; private Color successColor = new Color(0.5f, 1f, 0.5f, 0.3f); private Color warningColor = new Color(1f, 1f, 0.5f, 0.3f); private Color errorColor = new Color(1f, 0.5f, 0.5f, 0.3f); #endregion #region Window Initialization [MenuItem("Tools/Motion Capture Tools/File Renamer")] public static void ShowWindow() { var window = GetWindow("MoCap File Renamer"); window.minSize = new Vector2(450, 500); } private void OnEnable() { LoadPresets(); RefreshSelectedFiles(); Selection.selectionChanged += OnSelectionChanged; } private void OnDisable() { SavePresets(); Selection.selectionChanged -= OnSelectionChanged; } private void OnSelectionChanged() { RefreshSelectedFiles(); Repaint(); } private void InitializeStyles() { if (headerStyle == null) { headerStyle = new GUIStyle(EditorStyles.boldLabel) { fontSize = 14, alignment = TextAnchor.MiddleLeft }; } if (boxStyle == null) { boxStyle = new GUIStyle(EditorStyles.helpBox) { padding = new RectOffset(10, 10, 10, 10) }; } } #endregion #region GUI private void OnGUI() { InitializeStyles(); scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); DrawHeader(); DrawFileSelectionSection(); DrawNamingOptionsSection(); DrawPresetsSection(); DrawActionButtons(); EditorGUILayout.EndScrollView(); } private void DrawHeader() { EditorGUILayout.Space(5); EditorGUILayout.LabelField("🎬 MoCap File Renamer", headerStyle); EditorGUILayout.Space(5); } private void DrawFileSelectionSection() { EditorGUILayout.BeginVertical(boxStyle); // 선택된 파일 수 표시 GUIStyle countStyle = new GUIStyle(EditorStyles.boldLabel); countStyle.fontSize = 13; if (selectedFiles.Count > 0) { countStyle.normal.textColor = new Color(0.3f, 0.8f, 0.3f); EditorGUILayout.LabelField($"📁 {selectedFiles.Count} files selected", countStyle); } else { countStyle.normal.textColor = Color.gray; EditorGUILayout.LabelField($"📁 No files selected", countStyle); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); } private void DrawNamingOptionsSection() { EditorGUILayout.BeginVertical(boxStyle); EditorGUILayout.LabelField("⚙️ Naming Options", EditorStyles.boldLabel); EditorGUILayout.Space(5); // Naming mode namingMode = (NamingMode)EditorGUILayout.EnumPopup("Mode", namingMode); switch (namingMode) { case NamingMode.Suffix: customText = EditorGUILayout.TextField("Suffix", customText); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("_ok")) customText = "_ok"; if (GUILayout.Button("_final")) customText = "_final"; if (GUILayout.Button("_test")) customText = "_test"; EditorGUILayout.EndHorizontal(); break; case NamingMode.Prefix: customText = EditorGUILayout.TextField("Prefix", customText); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Take001")) customText = "Take001"; if (GUILayout.Button("MoCap")) customText = "MoCap"; if (GUILayout.Button("Test")) customText = "Test"; EditorGUILayout.EndHorizontal(); break; case NamingMode.Replace: customText = EditorGUILayout.TextField("New Name", customText); startNumber = EditorGUILayout.IntField("Start Number", startNumber); numberPadding = EditorGUILayout.IntSlider("Padding", numberPadding, 1, 5); break; case NamingMode.Sequential: customText = EditorGUILayout.TextField("Base Name", customText); startNumber = EditorGUILayout.IntField("Start Number", startNumber); numberPadding = EditorGUILayout.IntSlider("Padding", numberPadding, 1, 5); EditorGUILayout.LabelField($"Ex: {customText}{startNumber.ToString().PadLeft(numberPadding, '0')}", EditorStyles.miniLabel); break; } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); } private void DrawPresetsSection() { showPresetSection = EditorGUILayout.Foldout(showPresetSection, "💾 Presets", true, EditorStyles.foldoutHeader); if (showPresetSection) { EditorGUILayout.BeginVertical(boxStyle); if (presets.Count > 0) { for (int i = 0; i < presets.Count; i++) { EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(presets[i].presetName, GUILayout.Height(22))) { LoadPreset(i); } if (GUILayout.Button("×", GUILayout.Width(25), GUILayout.Height(22))) { presets.RemoveAt(i); SavePresets(); } EditorGUILayout.EndHorizontal(); } EditorGUILayout.Space(3); } EditorGUILayout.BeginHorizontal(); newPresetName = EditorGUILayout.TextField(newPresetName); if (GUILayout.Button("Save", GUILayout.Width(60))) { SaveCurrentAsPreset(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } EditorGUILayout.Space(5); } private void DrawActionButtons() { GUI.enabled = selectedFiles.Count > 0; GUI.backgroundColor = new Color(0.3f, 0.8f, 0.3f); if (GUILayout.Button("✓ Apply Rename", GUILayout.Height(50))) { ApplyRename(); } GUI.backgroundColor = Color.white; GUI.enabled = true; } #endregion #region File Selection private void RefreshSelectedFiles() { selectedFiles.Clear(); var selected = Selection.objects; foreach (var obj in selected) { string path = AssetDatabase.GetAssetPath(obj); if (!string.IsNullOrEmpty(path) && !AssetDatabase.IsValidFolder(path)) { selectedFiles.Add(obj); } } } private void HandleDragAndDrop(Rect dropArea) { Event evt = Event.current; if (dropArea.Contains(evt.mousePosition)) { if (evt.type == EventType.DragUpdated) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; evt.Use(); } else if (evt.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); foreach (var obj in DragAndDrop.objectReferences) { string path = AssetDatabase.GetAssetPath(obj); if (!string.IsNullOrEmpty(path) && !AssetDatabase.IsValidFolder(path)) { if (!selectedFiles.Contains(obj)) { selectedFiles.Add(obj); } } } evt.Use(); } } } #endregion #region Naming Logic private string GenerateNewName(string originalName, int index) { string baseName = originalName; string result = ""; switch (namingMode) { case NamingMode.Suffix: result = baseName + separator + customText; break; case NamingMode.Prefix: result = customText + separator + baseName; break; case NamingMode.Replace: string number = (startNumber + index).ToString().PadLeft(numberPadding, '0'); result = customText + separator + number; break; case NamingMode.Sequential: string seqNumber = (startNumber + index).ToString().PadLeft(numberPadding, '0'); result = customText + seqNumber; break; } // Remove invalid characters result = string.Join("_", result.Split(Path.GetInvalidFileNameChars())); return result; } private void ApplyRename() { if (selectedFiles.Count == 0) return; int successCount = 0; int failCount = 0; AssetDatabase.StartAssetEditing(); try { for (int i = 0; i < selectedFiles.Count; i++) { var file = selectedFiles[i]; if (file == null) continue; string assetPath = AssetDatabase.GetAssetPath(file); string directory = Path.GetDirectoryName(assetPath); string extension = Path.GetExtension(assetPath); string oldName = file.name; string newName = GenerateNewName(oldName, i); string newPath = Path.Combine(directory, newName + extension); // Check if file already exists if (File.Exists(newPath) && assetPath != newPath) { Debug.LogWarning($"Skipped (exists): {oldName}"); failCount++; continue; } // Rename asset string error = AssetDatabase.RenameAsset(assetPath, newName); if (string.IsNullOrEmpty(error)) { Debug.Log($"✓ {oldName} → {newName}"); successCount++; } else { Debug.LogError($"✗ {oldName}: {error}"); failCount++; } } } finally { AssetDatabase.StopAssetEditing(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } Debug.Log($"Rename Complete: {successCount} success, {failCount} failed"); // Clear selection selectedFiles.Clear(); } #endregion #region Presets private void SaveCurrentAsPreset() { var preset = new NamingPreset { presetName = newPresetName, mode = namingMode, customText = customText, startNumber = startNumber, numberPadding = numberPadding, separator = separator }; presets.Add(preset); SavePresets(); Debug.Log($"Preset saved: {newPresetName}"); } private void LoadPreset(int index) { if (index < 0 || index >= presets.Count) return; var preset = presets[index]; namingMode = preset.mode; customText = preset.customText; startNumber = preset.startNumber; numberPadding = preset.numberPadding; separator = preset.separator; Debug.Log($"Preset loaded: {preset.presetName}"); } private void SavePresets() { string json = JsonUtility.ToJson(new PresetList { presets = presets }, true); EditorPrefs.SetString("MotionCaptureFileRenamer_Presets", json); } private void LoadPresets() { string json = EditorPrefs.GetString("MotionCaptureFileRenamer_Presets", ""); if (!string.IsNullOrEmpty(json)) { try { var presetList = JsonUtility.FromJson(json); presets = presetList.presets ?? new List(); } catch { presets = new List(); } } } [Serializable] private class PresetList { public List presets = new List(); } #endregion }