구글 시트 매니저 로직 수정

This commit is contained in:
NTG_Lenovo 2025-07-15 12:59:53 +09:00
parent c04291fdd4
commit df3341158a
2 changed files with 94 additions and 67 deletions

View File

@ -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;

View File

@ -89,8 +89,6 @@ public static async Task<T> LoadSo<T>() where T : ScriptableObject
[Button("데이터 최신화"), EnableIf(nameof(CanFetchData))]
private async Task FetchGoogleSheet()
{
_availSheetArray = _availSheets.Split('/');
var prevLog = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(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("// <auto-generated>");
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<bool> InternalCreateGoogleSheetSoAsync()
string soPath = $"{soDirectory}/{sheetName}So.asset";
ScriptableObject soInstance = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soPath);
// 💡 잘못된 asset이면 삭제 후 새로 생성
if (soInstance == null)
{
if (File.Exists(soPath))
@ -629,11 +644,10 @@ private async Task<bool> 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<T> 생성
IList list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(dataType));
var dataArray = (JArray)sheetPair.Value;
@ -648,24 +662,33 @@ private async Task<bool> 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<bool> 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<bool> 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<ScriptableObject>(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);
}