207 lines
7.8 KiB
C#
207 lines
7.8 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using System;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Collections; // 추가: 리스트 처리를 위해 필요
|
|
using System.Collections.Generic;
|
|
|
|
namespace Northbound.Editor
|
|
{
|
|
public static class CSVToSOImporter
|
|
{
|
|
private static readonly string GAMEDATA_PATH = Path.Combine(Application.dataPath, "..", "GameData");
|
|
private static readonly string SO_BASE_PATH = "Assets/Data/ScriptableObjects";
|
|
|
|
[MenuItem("Tools/Data/Import All CSV")] // 메뉴 추가 (편의성)
|
|
public static void ImportAll()
|
|
{
|
|
Debug.Log("=== 전체 데이터 Import 시작 ===");
|
|
if (!Directory.Exists(GAMEDATA_PATH)) { Debug.LogError("GameData 폴더가 없습니다."); return; }
|
|
|
|
var csvFiles = Directory.GetFiles(GAMEDATA_PATH, "*.csv");
|
|
int totalSuccess = 0;
|
|
int totalFail = 0;
|
|
|
|
foreach (var csvPath in csvFiles)
|
|
{
|
|
if (csvPath.Contains("Backups")) continue;
|
|
string schemaName = Path.GetFileNameWithoutExtension(csvPath);
|
|
if (schemaName.StartsWith(".")) continue;
|
|
|
|
if (ImportSchema(schemaName)) totalSuccess++;
|
|
else totalFail++;
|
|
}
|
|
|
|
Debug.Log($"\n🎉 Import 완료: 성공 {totalSuccess}, 실패 {totalFail}");
|
|
}
|
|
|
|
public static bool ImportSchema(string schemaName)
|
|
{
|
|
string csvPath = Path.Combine(GAMEDATA_PATH, $"{schemaName}.csv");
|
|
string outputPath = Path.Combine(SO_BASE_PATH, schemaName);
|
|
|
|
if (!File.Exists(csvPath)) return false;
|
|
if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath);
|
|
|
|
string className = schemaName + "Data";
|
|
Type dataType = FindScriptableObjectType(className);
|
|
if (dataType == null) return false;
|
|
|
|
try
|
|
{
|
|
var lines = File.ReadAllLines(csvPath, System.Text.Encoding.UTF8);
|
|
if (lines.Length < 2) return false;
|
|
|
|
var headers = ParseCSVLine(lines[0]);
|
|
|
|
for (int lineIndex = 1; lineIndex < lines.Length; lineIndex++)
|
|
{
|
|
string line = lines[lineIndex].Trim();
|
|
if (string.IsNullOrEmpty(line)) continue;
|
|
|
|
var values = ParseCSVLine(line);
|
|
var so = ScriptableObject.CreateInstance(dataType);
|
|
|
|
for (int col = 0; col < headers.Length; col++)
|
|
{
|
|
if (col >= values.Length) break;
|
|
string fieldName = ToCamelCase(headers[col]);
|
|
FieldInfo field = dataType.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
|
|
if (field != null) SetFieldValue(so, field, values[col]);
|
|
}
|
|
|
|
string assetName = GetAssetName(so, lineIndex);
|
|
AssetDatabase.CreateAsset(so, Path.Combine(outputPath, $"{assetName}.asset"));
|
|
}
|
|
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"{schemaName} 오류: {e.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static void SetFieldValue(object obj, FieldInfo field, string value)
|
|
{
|
|
Type fieldType = field.FieldType;
|
|
|
|
// 1. 리스트 타입 처리 (List<T>)
|
|
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>))
|
|
{
|
|
Type elementType = fieldType.GetGenericArguments()[0];
|
|
IList list = (IList)field.GetValue(obj);
|
|
if (list == null)
|
|
{
|
|
list = (IList)Activator.CreateInstance(fieldType);
|
|
field.SetValue(obj, list);
|
|
}
|
|
list.Clear();
|
|
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
// 쉼표(,) 혹은 세미콜론(;)으로 분할 가능하게 설정
|
|
string[] items = value.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
foreach (var item in items)
|
|
{
|
|
object convertedItem = ConvertValue(item.Trim(), elementType);
|
|
if (convertedItem != null) list.Add(convertedItem);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
field.SetValue(obj, null);
|
|
return;
|
|
}
|
|
|
|
// 2. Nullable 처리
|
|
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
|
{
|
|
Type underlyingType = Nullable.GetUnderlyingType(fieldType);
|
|
field.SetValue(obj, ConvertValue(value, underlyingType));
|
|
}
|
|
else
|
|
{
|
|
// 3. 일반 타입 처리
|
|
field.SetValue(obj, ConvertValue(value, fieldType));
|
|
}
|
|
}
|
|
|
|
private static object ConvertValue(string value, Type targetType)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value)) return null;
|
|
|
|
try
|
|
{
|
|
if (targetType == typeof(int)) return int.Parse(value);
|
|
if (targetType == typeof(float)) return float.Parse(value);
|
|
if (targetType == typeof(bool))
|
|
{
|
|
string l = value.ToLower();
|
|
return l == "true" || l == "1" || l == "yes";
|
|
}
|
|
if (targetType == typeof(string)) return value.Replace("\\n", "\n");
|
|
return Convert.ChangeType(value, targetType);
|
|
}
|
|
catch { return null; }
|
|
}
|
|
|
|
// --- 유틸리티 메서드 (기존과 동일) ---
|
|
private static string[] ParseCSVLine(string line)
|
|
{
|
|
var result = new List<string>();
|
|
bool inQuotes = false;
|
|
string current = "";
|
|
for (int i = 0; i < line.Length; i++)
|
|
{
|
|
char c = line[i];
|
|
if (c == '"') inQuotes = !inQuotes;
|
|
else if (c == ',' && !inQuotes) { result.Add(current.Trim()); current = ""; }
|
|
else current += c;
|
|
}
|
|
result.Add(current.Trim());
|
|
return result.ToArray();
|
|
}
|
|
|
|
private static Type FindScriptableObjectType(string className)
|
|
{
|
|
string fullName = $"Northbound.Data.{className}";
|
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
Type type = assembly.GetType(fullName);
|
|
if (type != null && type.IsSubclassOf(typeof(ScriptableObject))) return type;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static string ToCamelCase(string snakeCase)
|
|
{
|
|
if (string.IsNullOrEmpty(snakeCase)) return snakeCase;
|
|
var parts = snakeCase.Split('_');
|
|
if (parts.Length == 1) return snakeCase;
|
|
string result = parts[0];
|
|
for (int i = 1; i < parts.Length; i++)
|
|
if (parts[i].Length > 0) result += char.ToUpper(parts[i][0]) + parts[i].Substring(1);
|
|
return result;
|
|
}
|
|
|
|
private static string GetAssetName(object so, int lineNumber)
|
|
{
|
|
Type type = so.GetType();
|
|
FieldInfo idField = type.GetField("id", BindingFlags.Public | BindingFlags.Instance);
|
|
if (idField != null)
|
|
{
|
|
var val = idField.GetValue(so);
|
|
if (val != null) return $"{type.Name.Replace("Data", "")}{val:D3}";
|
|
}
|
|
return $"Data_Row{lineNumber}";
|
|
}
|
|
}
|
|
} |