776 lines
36 KiB
C#
776 lines
36 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace UVC.UIToolkit.Editor
|
|
{
|
|
/// <summary>
|
|
/// 이미지 아이콘 폴더를 스캔하여 아이콘 경로 상수 클래스를 자동 생성하는 에디터 도구입니다.
|
|
/// </summary>
|
|
public class UTKImageIconsGenerator : EditorWindow
|
|
{
|
|
#region EditorPrefs Keys
|
|
|
|
private const string PrefKeyIconsFolder = "UTK_ImageIcons_IconsFolder";
|
|
private const string PrefKeyOutputPath = "UTK_ImageIcons_OutputPath";
|
|
private const string PrefKeyClassName = "UTK_ImageIcons_ClassName";
|
|
private const string PrefKeyNamespace = "UTK_ImageIcons_Namespace";
|
|
private const string PrefKeyIncludeSubfolders = "UTK_ImageIcons_IncludeSubfolders";
|
|
private const string PrefKeyGeneratePreview = "UTK_ImageIcons_GeneratePreview";
|
|
|
|
#endregion
|
|
|
|
#region Default Values
|
|
|
|
private const string DefaultIconsFolder = "Assets/Resources/Icons";
|
|
private const string DefaultOutputPath = "Assets/Scripts/UVC/UIToolkit/Common/UTKImageIcons.cs";
|
|
private const string DefaultClassName = "UTKImageIcons";
|
|
private const string DefaultNamespace = "UVC.UIToolkit";
|
|
|
|
#endregion
|
|
|
|
#region Supported Extensions
|
|
|
|
private static readonly string[] SupportedExtensions = { ".png", ".jpg", ".jpeg", ".tga", ".psd", ".gif", ".bmp" };
|
|
|
|
#endregion
|
|
|
|
#region Fields
|
|
|
|
private string _iconsFolder = DefaultIconsFolder;
|
|
private string _outputPath = DefaultOutputPath;
|
|
private string _className = DefaultClassName;
|
|
private string _namespace = DefaultNamespace;
|
|
private bool _includeSubfolders = true;
|
|
private bool _generatePreview = true;
|
|
|
|
private Vector2 _scrollPosition;
|
|
private List<IconInfo>? _previewIcons;
|
|
|
|
#endregion
|
|
|
|
private class IconInfo
|
|
{
|
|
public string FilePath { get; set; } = "";
|
|
public string FileName { get; set; } = "";
|
|
public string FieldName { get; set; } = "";
|
|
public string ResourcePath { get; set; } = "";
|
|
public string RelativePath { get; set; } = "";
|
|
public Texture2D? Preview { get; set; }
|
|
}
|
|
|
|
[MenuItem("Tools/UTK/Image Icons Generator")]
|
|
public static void ShowWindow()
|
|
{
|
|
var window = GetWindow<UTKImageIconsGenerator>("Image Icons Generator");
|
|
window.minSize = new Vector2(500, 400);
|
|
window.LoadSettings();
|
|
}
|
|
|
|
[MenuItem("Tools/UTK/Generate Image Icons Class (Quick)")]
|
|
public static void GenerateQuick()
|
|
{
|
|
var iconsFolder = EditorPrefs.GetString(PrefKeyIconsFolder, DefaultIconsFolder);
|
|
var outputPath = EditorPrefs.GetString(PrefKeyOutputPath, DefaultOutputPath);
|
|
var className = EditorPrefs.GetString(PrefKeyClassName, DefaultClassName);
|
|
var namespaceName = EditorPrefs.GetString(PrefKeyNamespace, DefaultNamespace);
|
|
var includeSubfolders = EditorPrefs.GetBool(PrefKeyIncludeSubfolders, true);
|
|
|
|
Generate(iconsFolder, outputPath, className, namespaceName, includeSubfolders);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
LoadSettings();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
ClearPreviewTextures();
|
|
}
|
|
|
|
private void LoadSettings()
|
|
{
|
|
_iconsFolder = EditorPrefs.GetString(PrefKeyIconsFolder, DefaultIconsFolder);
|
|
_outputPath = EditorPrefs.GetString(PrefKeyOutputPath, DefaultOutputPath);
|
|
_className = EditorPrefs.GetString(PrefKeyClassName, DefaultClassName);
|
|
_namespace = EditorPrefs.GetString(PrefKeyNamespace, DefaultNamespace);
|
|
_includeSubfolders = EditorPrefs.GetBool(PrefKeyIncludeSubfolders, true);
|
|
_generatePreview = EditorPrefs.GetBool(PrefKeyGeneratePreview, true);
|
|
}
|
|
|
|
private void SaveSettings()
|
|
{
|
|
EditorPrefs.SetString(PrefKeyIconsFolder, _iconsFolder);
|
|
EditorPrefs.SetString(PrefKeyOutputPath, _outputPath);
|
|
EditorPrefs.SetString(PrefKeyClassName, _className);
|
|
EditorPrefs.SetString(PrefKeyNamespace, _namespace);
|
|
EditorPrefs.SetBool(PrefKeyIncludeSubfolders, _includeSubfolders);
|
|
EditorPrefs.SetBool(PrefKeyGeneratePreview, _generatePreview);
|
|
}
|
|
|
|
private void ClearPreviewTextures()
|
|
{
|
|
_previewIcons = null;
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
EditorGUILayout.Space(10);
|
|
EditorGUILayout.LabelField("Image Icons Class Generator", EditorStyles.boldLabel);
|
|
EditorGUILayout.Space(5);
|
|
EditorGUILayout.HelpBox("이미지 아이콘 폴더를 스캔하여 Resources.Load로 사용할 수 있는 C# 정적 클래스를 생성합니다.", MessageType.Info);
|
|
EditorGUILayout.Space(10);
|
|
|
|
// 아이콘 폴더 경로
|
|
EditorGUILayout.BeginHorizontal();
|
|
_iconsFolder = EditorGUILayout.TextField("Icons Folder", _iconsFolder);
|
|
if (GUILayout.Button("...", GUILayout.Width(30)))
|
|
{
|
|
var path = EditorUtility.OpenFolderPanel("Select Icons Folder", "Assets", "");
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
// 상대 경로로 변환
|
|
if (path.StartsWith(Application.dataPath))
|
|
{
|
|
path = "Assets" + path.Substring(Application.dataPath.Length);
|
|
}
|
|
_iconsFolder = path;
|
|
ClearPreviewTextures();
|
|
}
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
// 출력 파일 경로
|
|
EditorGUILayout.BeginHorizontal();
|
|
_outputPath = EditorGUILayout.TextField("Output File", _outputPath);
|
|
if (GUILayout.Button("...", GUILayout.Width(30)))
|
|
{
|
|
var directory = Path.GetDirectoryName(_outputPath) ?? "Assets";
|
|
var filename = Path.GetFileName(_outputPath);
|
|
var path = EditorUtility.SaveFilePanel("Save Generated Class", directory, filename, "cs");
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
// 상대 경로로 변환
|
|
if (path.StartsWith(Application.dataPath))
|
|
{
|
|
path = "Assets" + path.Substring(Application.dataPath.Length);
|
|
}
|
|
_outputPath = path;
|
|
}
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space(5);
|
|
|
|
// 클래스명
|
|
_className = EditorGUILayout.TextField("Class Name", _className);
|
|
|
|
// 네임스페이스
|
|
_namespace = EditorGUILayout.TextField("Namespace", _namespace);
|
|
|
|
EditorGUILayout.Space(5);
|
|
|
|
// 옵션
|
|
_includeSubfolders = EditorGUILayout.Toggle("Include Subfolders", _includeSubfolders);
|
|
_generatePreview = EditorGUILayout.Toggle("Show Preview", _generatePreview);
|
|
|
|
EditorGUILayout.Space(10);
|
|
|
|
// 폴더 존재 여부 및 아이콘 수 표시
|
|
var folderExists = Directory.Exists(_iconsFolder);
|
|
var outputExists = File.Exists(_outputPath);
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Icons Folder:", GUILayout.Width(100));
|
|
EditorGUILayout.LabelField(folderExists ? "Found" : "Not Found",
|
|
folderExists ? EditorStyles.label : EditorStyles.boldLabel);
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
if (folderExists)
|
|
{
|
|
var iconCount = CountIcons(_iconsFolder, _includeSubfolders);
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Icons Found:", GUILayout.Width(100));
|
|
EditorGUILayout.LabelField($"{iconCount} images");
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Output File:", GUILayout.Width(100));
|
|
EditorGUILayout.LabelField(outputExists ? "Exists (will overwrite)" : "New file");
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space(10);
|
|
|
|
// 버튼들
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
GUI.enabled = folderExists;
|
|
if (GUILayout.Button("Scan & Preview", GUILayout.Height(25)))
|
|
{
|
|
SaveSettings();
|
|
ScanIcons();
|
|
}
|
|
|
|
if (GUILayout.Button("Generate", GUILayout.Height(25)))
|
|
{
|
|
SaveSettings();
|
|
Generate(_iconsFolder, _outputPath, _className, _namespace, _includeSubfolders);
|
|
}
|
|
GUI.enabled = true;
|
|
|
|
if (GUILayout.Button("Reset", GUILayout.Height(25)))
|
|
{
|
|
_iconsFolder = DefaultIconsFolder;
|
|
_outputPath = DefaultOutputPath;
|
|
_className = DefaultClassName;
|
|
_namespace = DefaultNamespace;
|
|
_includeSubfolders = true;
|
|
_generatePreview = true;
|
|
SaveSettings();
|
|
ClearPreviewTextures();
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
// 미리보기 영역
|
|
if (_generatePreview && _previewIcons != null && _previewIcons.Count > 0)
|
|
{
|
|
EditorGUILayout.Space(10);
|
|
EditorGUILayout.LabelField($"Preview ({_previewIcons.Count} icons)", EditorStyles.boldLabel);
|
|
|
|
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUILayout.ExpandHeight(true));
|
|
|
|
var iconSize = 48;
|
|
var padding = 4;
|
|
var labelHeight = 16;
|
|
var itemWidth = iconSize + padding * 2;
|
|
var itemHeight = iconSize + labelHeight + padding * 2;
|
|
|
|
var windowWidth = position.width - 20;
|
|
var columns = Mathf.Max(1, (int)(windowWidth / itemWidth));
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
var currentColumn = 0;
|
|
|
|
foreach (var icon in _previewIcons)
|
|
{
|
|
if (currentColumn >= columns)
|
|
{
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.BeginHorizontal();
|
|
currentColumn = 0;
|
|
}
|
|
|
|
EditorGUILayout.BeginVertical(GUILayout.Width(itemWidth), GUILayout.Height(itemHeight));
|
|
|
|
// 아이콘 이미지
|
|
var rect = GUILayoutUtility.GetRect(iconSize, iconSize);
|
|
if (icon.Preview != null)
|
|
{
|
|
GUI.DrawTexture(rect, icon.Preview, ScaleMode.ScaleToFit);
|
|
}
|
|
else
|
|
{
|
|
EditorGUI.DrawRect(rect, new Color(0.2f, 0.2f, 0.2f));
|
|
}
|
|
|
|
// 필드명
|
|
var style = new GUIStyle(EditorStyles.miniLabel)
|
|
{
|
|
alignment = TextAnchor.MiddleCenter,
|
|
clipping = TextClipping.Clip
|
|
};
|
|
EditorGUILayout.LabelField(icon.FieldName, style, GUILayout.Width(itemWidth), GUILayout.Height(labelHeight));
|
|
|
|
EditorGUILayout.EndVertical();
|
|
currentColumn++;
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
}
|
|
|
|
private void ScanIcons()
|
|
{
|
|
ClearPreviewTextures();
|
|
_previewIcons = GetIconInfos(_iconsFolder, _includeSubfolders);
|
|
|
|
// 프리뷰 텍스처 로드
|
|
foreach (var icon in _previewIcons)
|
|
{
|
|
icon.Preview = AssetDatabase.LoadAssetAtPath<Texture2D>(icon.FilePath);
|
|
}
|
|
}
|
|
|
|
private static int CountIcons(string folder, bool includeSubfolders)
|
|
{
|
|
var searchOption = includeSubfolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
|
var count = 0;
|
|
|
|
foreach (var ext in SupportedExtensions)
|
|
{
|
|
count += Directory.GetFiles(folder, $"*{ext}", searchOption).Length;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private static List<IconInfo> GetIconInfos(string folder, bool includeSubfolders)
|
|
{
|
|
var result = new List<IconInfo>();
|
|
var searchOption = includeSubfolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
|
var usedNames = new HashSet<string>();
|
|
|
|
// Resources 폴더 기준 경로 계산
|
|
var resourcesIndex = folder.IndexOf("Resources/", StringComparison.OrdinalIgnoreCase);
|
|
var isInResources = resourcesIndex >= 0;
|
|
var resourcesBasePath = isInResources ? folder.Substring(resourcesIndex + "Resources/".Length) : "";
|
|
|
|
foreach (var ext in SupportedExtensions)
|
|
{
|
|
var files = Directory.GetFiles(folder, $"*{ext}", searchOption);
|
|
foreach (var file in files)
|
|
{
|
|
var normalizedPath = file.Replace("\\", "/");
|
|
var fileName = Path.GetFileNameWithoutExtension(normalizedPath);
|
|
var relativePath = normalizedPath.Substring(folder.Length).TrimStart('/');
|
|
var relativeDir = Path.GetDirectoryName(relativePath)?.Replace("\\", "/") ?? "";
|
|
|
|
// Resources.Load용 경로 (확장자 제외)
|
|
string resourcePath;
|
|
if (isInResources)
|
|
{
|
|
var fullRelative = string.IsNullOrEmpty(resourcesBasePath)
|
|
? relativePath
|
|
: $"{resourcesBasePath}/{relativePath}";
|
|
resourcePath = Path.ChangeExtension(fullRelative, null);
|
|
}
|
|
else
|
|
{
|
|
resourcePath = normalizedPath; // Resources 외부면 전체 경로 사용
|
|
}
|
|
|
|
var fieldName = ConvertToFieldName(fileName, relativeDir, usedNames);
|
|
|
|
result.Add(new IconInfo
|
|
{
|
|
FilePath = normalizedPath,
|
|
FileName = fileName,
|
|
FieldName = fieldName,
|
|
ResourcePath = resourcePath,
|
|
RelativePath = relativePath
|
|
});
|
|
}
|
|
}
|
|
|
|
return result.OrderBy(i => i.FieldName).ToList();
|
|
}
|
|
|
|
public static void Generate(string iconsFolder, string outputPath, string className, string namespaceName, bool includeSubfolders)
|
|
{
|
|
if (!Directory.Exists(iconsFolder))
|
|
{
|
|
Debug.LogError($"아이콘 폴더를 찾을 수 없습니다: {iconsFolder}");
|
|
return;
|
|
}
|
|
|
|
var icons = GetIconInfos(iconsFolder, includeSubfolders);
|
|
if (icons.Count == 0)
|
|
{
|
|
Debug.LogError("아이콘을 찾을 수 없습니다.");
|
|
return;
|
|
}
|
|
|
|
var code = GenerateCode(icons, className, namespaceName, iconsFolder);
|
|
|
|
// 디렉토리 생성
|
|
var directory = Path.GetDirectoryName(outputPath);
|
|
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
|
{
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
File.WriteAllText(outputPath, code, Encoding.UTF8);
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log($"{className}.cs 생성 완료: {icons.Count}개 아이콘 ({outputPath})");
|
|
}
|
|
|
|
private static string GenerateCode(List<IconInfo> icons, string className, string namespaceName, string sourceFolder)
|
|
{
|
|
var sb = new StringBuilder();
|
|
|
|
// Resources 폴더 여부 확인
|
|
var isInResources = sourceFolder.Contains("Resources");
|
|
|
|
sb.AppendLine("// <auto-generated>");
|
|
sb.AppendLine("// 이 파일은 UTKImageIconsGenerator에 의해 자동 생성되었습니다.");
|
|
sb.AppendLine("// 직접 수정하지 마세요. Tools > UTK > Image Icons Generator 메뉴로 재생성하세요.");
|
|
sb.AppendLine($"// Source: {sourceFolder}");
|
|
sb.AppendLine("// </auto-generated>");
|
|
sb.AppendLine();
|
|
sb.AppendLine("#nullable enable");
|
|
sb.AppendLine("using System.Collections.Generic;");
|
|
sb.AppendLine("using System.Threading;");
|
|
sb.AppendLine("using Cysharp.Threading.Tasks;");
|
|
sb.AppendLine("using UnityEngine;");
|
|
sb.AppendLine();
|
|
sb.AppendLine($"namespace {namespaceName}");
|
|
sb.AppendLine("{");
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 이미지 아이콘 리소스 경로 상수 클래스입니다.");
|
|
sb.AppendLine($" /// 총 {icons.Count}개의 아이콘을 포함합니다.");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <example>");
|
|
sb.AppendLine(" /// <code>");
|
|
sb.AppendLine($" /// // 상수로 리소스 경로 사용");
|
|
sb.AppendLine($" /// string path = {className}.Home;");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 동기 Sprite 로드 (캐싱됨)");
|
|
sb.AppendLine($" /// Sprite sprite = {className}.LoadSprite({className}.Home);");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 비동기 Sprite 로드 (UniTask, 캐싱됨)");
|
|
sb.AppendLine($" /// Sprite? sprite = await {className}.LoadSpriteAsync({className}.Home, cancellationToken);");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 동기 Texture2D 로드 (캐싱됨)");
|
|
sb.AppendLine($" /// Texture2D texture = {className}.LoadTexture({className}.Settings);");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 비동기 Texture2D 로드 (UniTask, 캐싱됨)");
|
|
sb.AppendLine($" /// Texture2D? texture = await {className}.LoadTextureAsync({className}.Settings, cancellationToken);");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 이름으로 Sprite 로드");
|
|
sb.AppendLine($" /// Sprite icon = {className}.LoadSpriteByName(\"home\");");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 이름으로 비동기 Sprite 로드");
|
|
sb.AppendLine($" /// Sprite? icon = await {className}.LoadSpriteByNameAsync(\"home\", cancellationToken);");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 이름으로 경로 조회");
|
|
sb.AppendLine($" /// string iconPath = {className}.GetPath(\"settings\");");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 아이콘 존재 여부 확인");
|
|
sb.AppendLine($" /// if ({className}.HasIcon(\"search\")) {{ }}");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 전체 아이콘 이름 순회");
|
|
sb.AppendLine($" /// foreach (var name in {className}.GetAllIconNames()) {{ }}");
|
|
sb.AppendLine(" /// ");
|
|
sb.AppendLine($" /// // 캐시 클리어");
|
|
sb.AppendLine($" /// {className}.ClearCache();");
|
|
sb.AppendLine(" /// </code>");
|
|
sb.AppendLine(" /// </example>");
|
|
sb.AppendLine(" /// <remarks>");
|
|
sb.AppendLine(" /// <para><b>UXML에서 사용하기:</b></para>");
|
|
sb.AppendLine(" /// <para>UXML에서 이미지 아이콘을 사용하려면 USS에서 background-image를 설정합니다.</para>");
|
|
sb.AppendLine(" /// <code>");
|
|
sb.AppendLine(" /// /* USS 파일 */");
|
|
sb.AppendLine(" /// .my-icon {");
|
|
sb.AppendLine(" /// width: 24px;");
|
|
sb.AppendLine(" /// height: 24px;");
|
|
sb.AppendLine(" /// background-image: resource('UIToolkit/Images/icon_setting_22');");
|
|
sb.AppendLine(" /// }");
|
|
sb.AppendLine(" /// </code>");
|
|
sb.AppendLine(" /// <code>");
|
|
sb.AppendLine(" /// <!-- UXML 파일 -->");
|
|
sb.AppendLine(" /// <ui:VisualElement class=\"my-icon\" />");
|
|
sb.AppendLine(" /// </code>");
|
|
sb.AppendLine(" /// <para><b>C# 코드에서 UXML 요소에 이미지 적용:</b></para>");
|
|
sb.AppendLine(" /// <code>");
|
|
sb.AppendLine($" /// var iconElement = root.Q<VisualElement>(\"my-icon\");");
|
|
sb.AppendLine($" /// var texture = {className}.LoadTextureByName(\"icon_setting_22\");");
|
|
sb.AppendLine(" /// iconElement.style.backgroundImage = new StyleBackground(texture);");
|
|
sb.AppendLine(" /// </code>");
|
|
sb.AppendLine(" /// <para><b>Image 요소에서 사용:</b></para>");
|
|
sb.AppendLine(" /// <code>");
|
|
sb.AppendLine($" /// var image = root.Q<Image>(\"my-image\");");
|
|
sb.AppendLine($" /// image.sprite = {className}.LoadSpriteByName(\"btn_close_16\");");
|
|
sb.AppendLine(" /// </code>");
|
|
sb.AppendLine(" /// </remarks>");
|
|
sb.AppendLine($" public static class {className}");
|
|
sb.AppendLine(" {");
|
|
|
|
// 아이콘 경로 상수 생성
|
|
foreach (var icon in icons)
|
|
{
|
|
sb.AppendLine($" /// <summary>{icon.RelativePath}</summary>");
|
|
sb.AppendLine($" public const string {icon.FieldName} = \"{icon.ResourcePath}\";");
|
|
}
|
|
|
|
sb.AppendLine();
|
|
sb.AppendLine(" #region Lookup & Load");
|
|
sb.AppendLine();
|
|
|
|
// 이름으로 경로 조회 딕셔너리
|
|
sb.AppendLine(" private static readonly Dictionary<string, string> _pathsByName = new()");
|
|
sb.AppendLine(" {");
|
|
foreach (var icon in icons)
|
|
{
|
|
sb.AppendLine($" [\"{icon.FileName}\"] = \"{icon.ResourcePath}\",");
|
|
}
|
|
sb.AppendLine(" };");
|
|
sb.AppendLine();
|
|
|
|
// 캐시 딕셔너리
|
|
sb.AppendLine(" private static readonly Dictionary<string, Sprite?> _spriteCache = new();");
|
|
sb.AppendLine(" private static readonly Dictionary<string, Texture2D?> _textureCache = new();");
|
|
sb.AppendLine();
|
|
|
|
// 조회 메서드
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 아이콘 이름으로 리소스 경로를 조회합니다.");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"iconName\">아이콘 파일명 (확장자 제외)</param>");
|
|
sb.AppendLine(" /// <returns>리소스 경로, 없으면 빈 문자열</returns>");
|
|
sb.AppendLine(" public static string GetPath(string iconName)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" return _pathsByName.TryGetValue(iconName, out var path) ? path : string.Empty;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 아이콘이 존재하는지 확인합니다.");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" public static bool HasIcon(string iconName) => _pathsByName.ContainsKey(iconName);");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 모든 아이콘 이름 목록을 반환합니다.");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" public static IEnumerable<string> GetAllIconNames() => _pathsByName.Keys;");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 리소스 경로로 아이콘 이름을 조회합니다.");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"resourcePath\">리소스 경로 (예: Icons/Home)</param>");
|
|
sb.AppendLine(" /// <returns>아이콘 이름, 없으면 빈 문자열</returns>");
|
|
sb.AppendLine(" public static string GetIconName(string resourcePath)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" foreach (var kvp in _pathsByName)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (kvp.Value == resourcePath) return kvp.Key;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" return string.Empty;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 전체 아이콘 수를 반환합니다.");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine($" public static int Count => {icons.Count};");
|
|
sb.AppendLine();
|
|
|
|
// 동기 로드 메서드들
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 리소스 경로로 Sprite를 동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"resourcePath\">리소스 경로 (예: Icons/Home)</param>");
|
|
sb.AppendLine(" public static Sprite? LoadSprite(string resourcePath)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (_spriteCache.TryGetValue(resourcePath, out var cached))");
|
|
sb.AppendLine(" return cached;");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var sprite = Resources.Load<Sprite>(resourcePath);");
|
|
sb.AppendLine(" _spriteCache[resourcePath] = sprite;");
|
|
sb.AppendLine(" return sprite;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 리소스 경로로 Sprite를 비동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"resourcePath\">리소스 경로 (예: Icons/Home)</param>");
|
|
sb.AppendLine(" /// <param name=\"ct\">취소 토큰</param>");
|
|
sb.AppendLine(" public static async UniTask<Sprite?> LoadSpriteAsync(string resourcePath, CancellationToken ct = default)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (_spriteCache.TryGetValue(resourcePath, out var cached))");
|
|
sb.AppendLine(" return cached;");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var request = Resources.LoadAsync<Sprite>(resourcePath);");
|
|
sb.AppendLine(" await request.ToUniTask(cancellationToken: ct);");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var sprite = request.asset as Sprite;");
|
|
sb.AppendLine(" _spriteCache[resourcePath] = sprite;");
|
|
sb.AppendLine(" return sprite;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 리소스 경로로 Texture2D를 동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"resourcePath\">리소스 경로 (예: Icons/Home)</param>");
|
|
sb.AppendLine(" public static Texture2D? LoadTexture(string resourcePath)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (_textureCache.TryGetValue(resourcePath, out var cached))");
|
|
sb.AppendLine(" return cached;");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var texture = Resources.Load<Texture2D>(resourcePath);");
|
|
sb.AppendLine(" _textureCache[resourcePath] = texture;");
|
|
sb.AppendLine(" return texture;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 리소스 경로로 Texture2D를 비동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"resourcePath\">리소스 경로 (예: Icons/Home)</param>");
|
|
sb.AppendLine(" /// <param name=\"ct\">취소 토큰</param>");
|
|
sb.AppendLine(" public static async UniTask<Texture2D?> LoadTextureAsync(string resourcePath, CancellationToken ct = default)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (_textureCache.TryGetValue(resourcePath, out var cached))");
|
|
sb.AppendLine(" return cached;");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var request = Resources.LoadAsync<Texture2D>(resourcePath);");
|
|
sb.AppendLine(" await request.ToUniTask(cancellationToken: ct);");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var texture = request.asset as Texture2D;");
|
|
sb.AppendLine(" _textureCache[resourcePath] = texture;");
|
|
sb.AppendLine(" return texture;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 아이콘 이름으로 Sprite를 동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"iconName\">아이콘 파일명 (확장자 제외)</param>");
|
|
sb.AppendLine(" public static Sprite? LoadSpriteByName(string iconName)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var path = GetPath(iconName);");
|
|
sb.AppendLine(" return string.IsNullOrEmpty(path) ? null : LoadSprite(path);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 아이콘 이름으로 Sprite를 비동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"iconName\">아이콘 파일명 (확장자 제외)</param>");
|
|
sb.AppendLine(" /// <param name=\"ct\">취소 토큰</param>");
|
|
sb.AppendLine(" public static async UniTask<Sprite?> LoadSpriteByNameAsync(string iconName, CancellationToken ct = default)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var path = GetPath(iconName);");
|
|
sb.AppendLine(" return string.IsNullOrEmpty(path) ? null : await LoadSpriteAsync(path, ct);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 아이콘 이름으로 Texture2D를 동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"iconName\">아이콘 파일명 (확장자 제외)</param>");
|
|
sb.AppendLine(" public static Texture2D? LoadTextureByName(string iconName)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var path = GetPath(iconName);");
|
|
sb.AppendLine(" return string.IsNullOrEmpty(path) ? null : LoadTexture(path);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 아이콘 이름으로 Texture2D를 비동기로 로드합니다. (캐싱됨)");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" /// <param name=\"iconName\">아이콘 파일명 (확장자 제외)</param>");
|
|
sb.AppendLine(" /// <param name=\"ct\">취소 토큰</param>");
|
|
sb.AppendLine(" public static async UniTask<Texture2D?> LoadTextureByNameAsync(string iconName, CancellationToken ct = default)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var path = GetPath(iconName);");
|
|
sb.AppendLine(" return string.IsNullOrEmpty(path) ? null : await LoadTextureAsync(path, ct);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 캐시를 클리어합니다.");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(" public static void ClearCache()");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" _spriteCache.Clear();");
|
|
sb.AppendLine(" _textureCache.Clear();");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" #endregion");
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine("}");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string ConvertToFieldName(string fileName, string relativeDir, HashSet<string> usedNames)
|
|
{
|
|
var sb = new StringBuilder();
|
|
|
|
// 서브폴더가 있으면 접두사로 추가
|
|
if (!string.IsNullOrEmpty(relativeDir))
|
|
{
|
|
var dirParts = relativeDir.Split('/');
|
|
foreach (var part in dirParts)
|
|
{
|
|
AppendPascalCase(sb, part);
|
|
}
|
|
sb.Append('_');
|
|
}
|
|
|
|
// 파일명 변환
|
|
AppendPascalCase(sb, fileName);
|
|
|
|
var result = sb.ToString();
|
|
|
|
// 숫자로 시작하면 앞에 _ 추가
|
|
if (result.Length > 0 && char.IsDigit(result[0]))
|
|
{
|
|
result = "_" + result;
|
|
}
|
|
|
|
// 빈 문자열이면 기본값
|
|
if (string.IsNullOrEmpty(result))
|
|
{
|
|
result = "_Icon";
|
|
}
|
|
|
|
// 중복 처리
|
|
var originalResult = result;
|
|
var counter = 2;
|
|
while (usedNames.Contains(result))
|
|
{
|
|
result = $"{originalResult}_{counter}";
|
|
counter++;
|
|
}
|
|
|
|
usedNames.Add(result);
|
|
return result;
|
|
}
|
|
|
|
private static void AppendPascalCase(StringBuilder sb, string input)
|
|
{
|
|
// 특수문자를 구분자로 사용
|
|
var words = input.Split(new[] { '_', '-', ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
foreach (var word in words)
|
|
{
|
|
if (word.Length == 0) continue;
|
|
|
|
// 첫 글자 대문자
|
|
sb.Append(char.ToUpper(word[0]));
|
|
if (word.Length > 1)
|
|
{
|
|
// 나머지는 소문자로 (단, 연속 대문자면 유지)
|
|
for (int i = 1; i < word.Length; i++)
|
|
{
|
|
if (char.IsUpper(word[i]) && i + 1 < word.Length && char.IsUpper(word[i + 1]))
|
|
{
|
|
sb.Append(word[i]);
|
|
}
|
|
else
|
|
{
|
|
sb.Append(char.ToLower(word[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|