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
}