diff --git a/Assets/_DDD/_Scripts/AssetManagement/AssetManager.cs b/Assets/_DDD/_Scripts/AssetManagement/AssetManager.cs index 680148187..eaa4739fc 100644 --- a/Assets/_DDD/_Scripts/AssetManagement/AssetManager.cs +++ b/Assets/_DDD/_Scripts/AssetManagement/AssetManager.cs @@ -1,7 +1,9 @@ using System; using System.Linq; using System.Threading.Tasks; +#if UNITY_EDITOR using UnityEditor.AddressableAssets; +#endif using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; diff --git a/Assets/_DDD/_Scripts/GenerateGoogleSheet/Core/GoogleSheetManager.cs b/Assets/_DDD/_Scripts/GenerateGoogleSheet/Core/GoogleSheetManager.cs index 81e9a3ae1..79d3d55c7 100644 --- a/Assets/_DDD/_Scripts/GenerateGoogleSheet/Core/GoogleSheetManager.cs +++ b/Assets/_DDD/_Scripts/GenerateGoogleSheet/Core/GoogleSheetManager.cs @@ -89,8 +89,6 @@ public static async Task LoadSo() where T : ScriptableObject [Button("데이터 최신화"), EnableIf(nameof(CanFetchData))] private async Task FetchGoogleSheet() { - - _availSheetArray = _availSheets.Split('/'); var prevLog = AssetDatabase.LoadAssetAtPath(ChangeLogAssetPath); @@ -366,14 +364,17 @@ private void GenerateClassFilesPerSheet(string jsonInput) foreach (var property in ((JObject)items[i]).Properties()) { string rawName = property.Name; - - // ✅ Enum 타입 여부 판단 string enumType = null; - if (rawName.EndsWith("_Enum")) + + // ✅ 단일 필드 Enum: Cookware:Enum + if (rawName.Contains(":Enum")) { - enumType = rawName.Contains(":") - ? rawName.Split(':')[1].Replace("_Enum", "") - : rawName.Replace("_Enum", ""); + enumType = rawName.Split(':')[0]; // 필드 이름이 곧 Enum 이름 + } + // ✅ 공통 Enum: Taste1:Taste_Enum + else if (rawName.Contains(":") && rawName.EndsWith("_Enum")) + { + enumType = rawName.Split(':')[1].Replace("_Enum", ""); } if (!string.IsNullOrEmpty(enumType)) @@ -389,17 +390,17 @@ private void GenerateClassFilesPerSheet(string jsonInput) } } - // EnumTypes.cs 생성 + // ✅ EnumTypes.cs 생성 StringBuilder enumCode = new(); enumCode.AppendLine("// "); - enumCode.AppendLine("using System;\n"); + enumCode.AppendLine("using System;"); enumCode.AppendLine(); enumCode.AppendLine($"namespace {_namespace}"); enumCode.AppendLine("{"); foreach (var kvp in enumCandidates) { - enumCode.AppendLine($" public enum {kvp.Key} \n{{"); + enumCode.AppendLine($" public enum {kvp.Key} \n {{"); enumCode.AppendLine(" None = 0,"); int index = 1; foreach (string value in kvp.Value) @@ -410,6 +411,7 @@ private void GenerateClassFilesPerSheet(string jsonInput) enumCode.AppendLine(" }\n"); } + enumCode.AppendLine("}"); File.WriteAllText($"{BaseAssetPath}/EnumTypes.cs", enumCode.ToString()); @@ -421,7 +423,7 @@ private void GenerateClassFilesPerSheet(string jsonInput) AssetDatabase.ImportAsset(ClassedFullPath); } - // 시트별 클래스 생성 + // ✅ 시트별 클래스/So 생성 foreach (var jObject in jsonObject) { string className = jObject.Key; @@ -451,10 +453,10 @@ private string GenerateSoClassCode(string className) "using System.Collections.Generic;\n" + "using UnityEngine;\n\n" + $"namespace {_namespace}\n" + - "{\n" + + $"{{\n" + $" [CreateAssetMenu(fileName = \"{className}So\", menuName = \"GoogleSheet/{className}So\")]\n" + - $" public class {className}So : ScriptableObject \n" + - $" {{\n public List<{className}> {className}List;\n }}\n}}\n"; + $" public class {className}So : DataSo<{className}> {{ }}\n" + + $"}}"; } private string GenerateDataClassCode(string className, JArray items) @@ -470,7 +472,7 @@ private string GenerateDataClassCode(string className, JArray items) sb.AppendLine($"namespace {_namespace}"); sb.AppendLine("{"); sb.AppendLine(" [Serializable]"); - sb.AppendLine($" public class {className}"); + sb.AppendLine($" public class {className} : IId"); sb.AppendLine(" {"); int count = sampleRow.Properties().Count(); @@ -487,22 +489,27 @@ private string GenerateDataClassCode(string className, JArray items) string fieldName = rawName; string explicitType = null; - if (rawName.Contains(":")) + if (rawName.Contains(":Enum")) + { + fieldName = rawName.Split(':')[0]; + explicitType = fieldName; + } + else if (rawName.Contains(":") && rawName.EndsWith("_Enum")) { var parts = rawName.Split(':'); fieldName = parts[0]; explicitType = parts[1].Replace("_Enum", ""); } - else if (rawName.EndsWith("_Enum")) + else if (rawName.Contains(":")) { - fieldName = rawName.Replace("_Enum", ""); - explicitType = fieldName; + var parts = rawName.Split(':'); + fieldName = parts[0]; + explicitType = parts[1]; } types[i] = explicitType ?? GetCSharpType(prop.Value.Type); names[i] = fieldName; tooltips[i] ??= commentRow.TryGetValue(rawName, out var tip) ? tip.ToString() : ""; - i++; } } @@ -515,11 +522,20 @@ private string GenerateDataClassCode(string className, JArray items) sb.AppendLine($" [Tooltip(\"{tooltips[i]}\")]"); } - sb.AppendLine($" public {types[i]} {names[i]};\n"); + if (names[i] == "Id" && types[i] == "string") + { + sb.AppendLine(" [field: SerializeField]"); + sb.AppendLine(" public string Id { get; set; }\n"); + } + else + { + sb.AppendLine($" public {types[i]} {names[i]};\n"); + } } sb.AppendLine(" }"); sb.AppendLine("}"); + return sb.ToString(); } @@ -616,7 +632,6 @@ private async Task InternalCreateGoogleSheetSoAsync() string soPath = $"{soDirectory}/{sheetName}So.asset"; ScriptableObject soInstance = AssetDatabase.LoadAssetAtPath(soPath); - // 💡 잘못된 asset이면 삭제 후 새로 생성 if (soInstance == null) { if (File.Exists(soPath)) @@ -629,11 +644,10 @@ private async Task InternalCreateGoogleSheetSoAsync() AssetDatabase.CreateAsset(soInstance, soPath); AssetDatabase.SaveAssets(); AssetDatabase.ImportAsset(soPath, ImportAssetOptions.ForceSynchronousImport); - await Task.Delay(100); // meta 확정 시간 확보 + await Task.Delay(100); AssetDatabase.Refresh(); } - // 🧠 Sprite나 Enum 값을 반영한 List 생성 IList list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(dataType)); var dataArray = (JArray)sheetPair.Value; @@ -648,24 +662,33 @@ private async Task InternalCreateGoogleSheetSoAsync() string fieldName = rawName; string explicitType = null; - if (rawName.Contains(":")) + if (rawName.Contains(":Enum")) { - var split = rawName.Split(':'); - fieldName = split[0]; - explicitType = split[1].Replace("_Enum", ""); - } - else if (rawName.EndsWith("_Enum")) - { - fieldName = rawName.Replace("_Enum", ""); + fieldName = rawName.Split(':')[0]; explicitType = fieldName; } + else if (rawName.Contains(":") && rawName.EndsWith("_Enum")) + { + var parts = rawName.Split(':'); + fieldName = parts[0]; + explicitType = parts[1].Replace("_Enum", ""); + } + else if (rawName.Contains(":")) + { + var parts = rawName.Split(':'); + fieldName = parts[0]; + explicitType = parts[1]; + } FieldInfo field = dataType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (field == null) + PropertyInfo property = dataType.GetProperty(fieldName, + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + if (field == null && property == null) { - Debug.LogWarning($"[GoogleSheetManager] 필드 누락: {dataType.Name}.{fieldName}"); + Debug.LogWarning($"[GoogleSheetManager] 필드/프로퍼티 누락: {dataType.Name}.{fieldName}"); continue; } @@ -673,12 +696,12 @@ private async Task InternalCreateGoogleSheetSoAsync() { object value; - // ✅ Sprite 처리 (주소로부터 비동기 로드) - if (explicitType == "Sprite" && field.FieldType == typeof(Sprite)) + // Sprite, Color 등 기존 로직 유지 + if (explicitType == "Sprite" && (field?.FieldType == typeof(Sprite) || + property?.PropertyType == typeof(Sprite))) { string spriteKey = prop.Value.ToString().Trim(); - if (string.IsNullOrEmpty(spriteKey)) - continue; + if (string.IsNullOrEmpty(spriteKey)) continue; if (!AssetManager.HasLabel(spriteKey, "Sprite")) { @@ -696,68 +719,62 @@ private async Task InternalCreateGoogleSheetSoAsync() } value = handle.Result; - field.SetValue(dataInstance, value); - continue; // 이후 변환 스킵 } - - // ✅ Enum 처리 - if (field.FieldType.IsEnum) + else if ((field?.FieldType.IsEnum ?? false) || (property?.PropertyType.IsEnum ?? false)) { + Type enumType = field?.FieldType ?? property?.PropertyType; string formatted = NormalizeEnumKey(prop.Value.ToString()); - value = Enum.TryParse(field.FieldType, formatted, out var parsed) + value = Enum.TryParse(enumType, formatted, out var parsed) ? parsed - : Activator.CreateInstance(field.FieldType); + : Activator.CreateInstance(enumType); } - // ✅ Color 처리 - else if (field.FieldType == typeof(Color)) + else if ((field?.FieldType == typeof(Color)) || (property?.PropertyType == typeof(Color))) { value = ColorUtility.TryParseHtmlString(prop.Value.ToString(), out var color) ? color : Color.white; } - // ✅ 기본 타입 처리 + else if ((field?.FieldType == typeof(string)) || (property?.PropertyType == typeof(string))) + { + value = prop.Value.ToString(); + } else { - value = Convert.ChangeType(prop.Value.ToString(), field.FieldType); + Type targetType = field?.FieldType ?? property?.PropertyType; + value = Convert.ChangeType(prop.Value.ToString(), targetType); } - field.SetValue(dataInstance, value); + if (field != null) + field.SetValue(dataInstance, value); + else if (property != null && property.CanWrite) + property.SetValue(dataInstance, value); } catch (Exception e) { - Debug.LogWarning( - $"[GoogleSheetManager] 값 할당 실패: {fieldName} = {prop.Value} ({field.FieldType}) → {e.Message}"); + Debug.LogWarning($"[GoogleSheetManager] 값 할당 실패: {fieldName} = {prop.Value} → {e.Message}"); } } list.Add(dataInstance); } - // ✅ 리스트 필드에 값 설정 - FieldInfo listField = soType.GetField($"{sheetName}List", + // ✅ SetDataList() 호출 + MethodInfo setMethod = soType.GetMethod("SetDataList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - - if (listField != null) + if (setMethod != null) { - object existingListObj = listField.GetValue(soInstance); - if (existingListObj is IList existingList) - { - existingList.Clear(); // ✅ 기존 리스트를 완전히 비움 - } - - listField.SetValue(soInstance, list); + setMethod.Invoke(soInstance, new object[] { list }); EditorUtility.SetDirty(soInstance); } else { - Debug.LogError($"[GoogleSheetManager] {soType.Name}에 {sheetName}List 필드가 없습니다."); + Debug.LogError($"[GoogleSheetManager] {soType.Name}에 SetDataList 메서드가 없습니다."); allSuccess = false; } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); - // ✅ Addressables 등록은 마지막에, 유효한 경우만 if (AssetDatabase.LoadAssetAtPath(soPath) != null) { GoogleSheetAddressableAutoSetup.AutoRegisterSo(soPath); @@ -781,9 +798,17 @@ private Type FindTypeByName(string sheetName) private string NormalizeEnumKey(string input) { - string validName = System.Text.RegularExpressions.Regex.Replace(input, @"[^a-zA-Z0-9_]", "_"); + if (string.IsNullOrEmpty(input)) + return "None"; + + // 특수문자 및 공백을 밑줄(_)로 치환 + string validName = System.Text.RegularExpressions.Regex.Replace(input, @"[^a-zA-Z0-9_]+", "_"); + + // 숫자로 시작하는 경우 밑줄 추가 if (char.IsDigit(validName[0])) validName = "_" + validName; + + // 첫 글자 대문자화 return char.ToUpper(validName[0]) + validName.Substring(1); }