diff --git a/Assembly-CSharp-Editor.csproj b/Assembly-CSharp-Editor.csproj
index e4ffec5..33273b5 100644
--- a/Assembly-CSharp-Editor.csproj
+++ b/Assembly-CSharp-Editor.csproj
@@ -51,12 +51,9 @@
-
-
-
diff --git a/Assets/Editor.meta b/Assets/Editor.meta
deleted file mode 100644
index 7afea97..0000000
--- a/Assets/Editor.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 208723e08bdfdc347bc1af53f4be5c3c
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Editor/DataImporter.meta b/Assets/Editor/DataImporter.meta
deleted file mode 100644
index 3a49ee8..0000000
--- a/Assets/Editor/DataImporter.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 8f4724b7534e0a443a8eabbe6f5483ad
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Editor/DataImporter/CSVDebugger.cs b/Assets/Editor/DataImporter/CSVDebugger.cs
deleted file mode 100644
index 4730533..0000000
--- a/Assets/Editor/DataImporter/CSVDebugger.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// Assets/Editor/DataImporter/CSVDebugger.cs
-
-using UnityEngine;
-using UnityEditor;
-using System.IO;
-using System.Text;
-
-namespace Northbound.Editor
-{
- public static class CSVDebugger
- {
- [MenuItem("Northbound/Debug CSV Files")]
- public static void DebugCSVFiles()
- {
- string gameDataPath = Path.Combine(Application.dataPath, "..", "GameData");
- var csvFiles = Directory.GetFiles(gameDataPath, "*.csv");
-
- Debug.Log("=== CSV 파일 디버그 ===\n");
-
- foreach (var csvPath in csvFiles)
- {
- string fileName = Path.GetFileName(csvPath);
-
- if (fileName.StartsWith("."))
- continue;
-
- Debug.Log($"📄 파일: {fileName}");
-
- // 파일 크기
- FileInfo fileInfo = new FileInfo(csvPath);
- Debug.Log($" 📊 크기: {fileInfo.Length} bytes");
-
- // 인코딩 테스트
- try
- {
- // UTF-8로 읽기
- var lines = File.ReadAllLines(csvPath, Encoding.UTF8);
- Debug.Log($" ✅ UTF-8 읽기 성공: {lines.Length}줄");
-
- // ⭐ 모든 줄 출력
- for (int i = 0; i < lines.Length; i++)
- {
- string line = lines[i];
- Debug.Log($" 📋 [{i}] 길이:{line.Length} | 내용: '{line}'");
-
- // 특수문자 확인
- if (string.IsNullOrWhiteSpace(line))
- {
- Debug.Log($" ⚠️ 빈 줄 또는 공백만 있음");
- }
-
- // 바이트 출력 (첫 20바이트만)
- byte[] bytes = Encoding.UTF8.GetBytes(line);
- string byteStr = "";
- for (int j = 0; j < Mathf.Min(bytes.Length, 20); j++)
- {
- byteStr += $"{bytes[j]:X2} ";
- }
- if (bytes.Length > 20) byteStr += "...";
- Debug.Log($" 바이트: {byteStr}");
- }
-
- // BOM 체크
- byte[] fileBytes = File.ReadAllBytes(csvPath);
- if (fileBytes.Length >= 3 && fileBytes[0] == 0xEF && fileBytes[1] == 0xBB && fileBytes[2] == 0xBF)
- {
- Debug.Log($" ℹ️ UTF-8 BOM 있음");
- }
- else
- {
- Debug.Log($" ℹ️ BOM 없음");
- }
-
- // 전체 파일 바이트 (처음 100바이트만)
- string fileBytesStr = "";
- for (int i = 0; i < Mathf.Min(fileBytes.Length, 100); i++)
- {
- fileBytesStr += $"{fileBytes[i]:X2} ";
- if ((i + 1) % 20 == 0) fileBytesStr += "\n ";
- }
- Debug.Log($" 📦 파일 바이트 (처음 100):\n {fileBytesStr}");
- }
- catch (System.Exception e)
- {
- Debug.LogError($" ❌ 읽기 실패: {e.Message}");
- }
-
- Debug.Log("");
- }
-
- Debug.Log("=== 디버그 완료 ===");
- }
- }
-}
\ No newline at end of file
diff --git a/Assets/Editor/DataImporter/CSVDebugger.cs.meta b/Assets/Editor/DataImporter/CSVDebugger.cs.meta
deleted file mode 100644
index ec4952f..0000000
--- a/Assets/Editor/DataImporter/CSVDebugger.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 95247e4274c204446ac0f9c45c334c56
\ No newline at end of file
diff --git a/Assets/Editor/DataImporter/CSVToSOImporter.cs b/Assets/Editor/DataImporter/CSVToSOImporter.cs
deleted file mode 100644
index 8e354e8..0000000
--- a/Assets/Editor/DataImporter/CSVToSOImporter.cs
+++ /dev/null
@@ -1,320 +0,0 @@
-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";
- private static readonly string PREFAB_BASE_PATH = "Assets/Prefabs";
- private static readonly string TEMPLATE_BASE_PATH = "Assets/Data/Templates";
-
- private static System.Collections.Generic.Dictionary prefabSetups =
- new System.Collections.Generic.Dictionary();
-
- static CSVToSOImporter()
- {
- RegisterPrefabSetups();
- }
-
- private static void RegisterPrefabSetups()
- {
- prefabSetups.Clear();
- prefabSetups["Monster"] = new MonsterPrefabSetup();
-
- // To add new data types, create a class implementing IPrefabSetup
- // Example:
- // prefabSetups["Tower"] = new TowerPrefabSetup();
- // prefabSetups["Player"] = new PlayerPrefabSetup();
- }
-
- [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]);
- var createdSOs = new List();
-
- 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);
- string soPath = Path.Combine(outputPath, $"{assetName}.asset");
-
- AssetDatabase.CreateAsset(so, soPath);
- createdSOs.Add(so);
-
- GenerateOrUpdatePrefab(schemaName, assetName, so, lineIndex);
- }
-
- 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)
- 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 int GetColumnIndex(string[] headers, string columnName)
- {
- for (int i = 0; i < headers.Length; i++)
- {
- if (headers[i].Equals(columnName, System.StringComparison.OrdinalIgnoreCase))
- {
- return i;
- }
- }
- return -1;
- }
-
- private static void GenerateOrUpdatePrefab(string schemaName, string prefabName, ScriptableObject so, int lineNumber)
- {
- string prefabOutputPath = Path.Combine(PREFAB_BASE_PATH, schemaName);
- if (!Directory.Exists(prefabOutputPath))
- {
- Directory.CreateDirectory(prefabOutputPath);
- }
-
- string prefabPath = Path.Combine(prefabOutputPath, $"{prefabName}.prefab");
- IPrefabSetup prefabSetup = GetPrefabSetup(schemaName);
-
- if (prefabSetup == null)
- {
- Debug.LogWarning($"[CSVImporter] No prefab setup found for {schemaName}, skipping prefab generation");
- return;
- }
-
- GameObject template = LoadTemplate(schemaName);
- if (template == null)
- {
- Debug.LogWarning($"[CSVImporter] No template found for {schemaName}, skipping prefab generation");
- return;
- }
-
- if (AssetDatabase.LoadAssetAtPath(prefabPath) != null)
- {
- UpdateExistingPrefab(prefabPath, so, prefabSetup);
- }
- else
- {
- GameObject prefabInstance = GameObject.Instantiate(template);
- prefabInstance.name = prefabName;
- PrefabUtility.SaveAsPrefabAsset(prefabInstance, prefabPath);
- GameObject.DestroyImmediate(prefabInstance);
-
- UpdateExistingPrefab(prefabPath, so, prefabSetup);
-
- Debug.Log($"[CSVImporter] Created prefab: {prefabPath}");
- }
-
- AssetDatabase.SaveAssets();
- }
-
- private static GameObject LoadTemplate(string schemaName)
- {
- string templatePath = Path.Combine(TEMPLATE_BASE_PATH, $"{schemaName}Template.prefab");
- return AssetDatabase.LoadAssetAtPath(templatePath);
- }
-
- private static IPrefabSetup GetPrefabSetup(string schemaName)
- {
- prefabSetups.TryGetValue(schemaName, out IPrefabSetup setup);
- return setup;
- }
-
- private static void UpdateExistingPrefab(string prefabPath, ScriptableObject so, IPrefabSetup prefabSetup)
- {
- GameObject prefabContents = PrefabUtility.LoadPrefabContents(prefabPath);
-
- prefabSetup.SetupPrefab(prefabContents, so);
-
- PrefabUtility.SaveAsPrefabAsset(prefabContents, prefabPath);
- GameObject.DestroyImmediate(prefabContents);
- }
-
- private static void RemoveOldModel(GameObject prefab)
- {
- Transform oldModel = prefab.transform.Find("Model");
- if (oldModel != null)
- {
- GameObject.DestroyImmediate(oldModel.gameObject);
- }
- }
-
- // --- 유틸리티 메서드 (기존과 동일) ---
- private static string[] ParseCSVLine(string line)
- {
- var result = new List();
- 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}";
- }
- }
-}
\ No newline at end of file
diff --git a/Assets/Editor/DataImporter/CSVToSOImporter.cs.meta b/Assets/Editor/DataImporter/CSVToSOImporter.cs.meta
deleted file mode 100644
index 8d6a1a9..0000000
--- a/Assets/Editor/DataImporter/CSVToSOImporter.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 79f372743463a5e4cb3a9b915de9bebe
\ No newline at end of file
diff --git a/Assets/Editor/DataImporter/ImporterWindow.cs b/Assets/Editor/DataImporter/ImporterWindow.cs
deleted file mode 100644
index e77a651..0000000
--- a/Assets/Editor/DataImporter/ImporterWindow.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-// Assets/Editor/DataImporter/ImporterWindow.cs
-
-using UnityEngine;
-using UnityEditor;
-using System.IO;
-using System.Linq;
-
-namespace Northbound.Editor
-{
- public class ImporterWindow : EditorWindow
- {
- private Vector2 scrollPosition;
-
- [MenuItem("Northbound/Data Importer")]
- public static void ShowWindow()
- {
- var window = GetWindow("Data Importer");
- window.minSize = new Vector2(400, 300);
- }
-
- private void OnGUI()
- {
- GUILayout.Label("CSV → ScriptableObject Importer", EditorStyles.boldLabel);
- GUILayout.Space(10);
-
- EditorGUILayout.HelpBox(
- "GameData 폴더의 CSV 파일을 ScriptableObject로 변환합니다.\n" +
- "자동 생성된 C# 클래스를 사용합니다.",
- MessageType.Info
- );
-
- GUILayout.Space(10);
-
- scrollPosition = GUILayout.BeginScrollView(scrollPosition);
-
- string gameDataPath = Path.Combine(Application.dataPath, "..", "GameData");
-
- if (!Directory.Exists(gameDataPath))
- {
- EditorGUILayout.HelpBox(
- "GameData 폴더를 찾을 수 없습니다.",
- MessageType.Warning
- );
- }
- else
- {
- var csvFiles = Directory.GetFiles(gameDataPath, "*.csv")
- .Where(f => !f.Contains("Backups") && !Path.GetFileName(f).StartsWith("."))
- .ToArray();
-
- if (csvFiles.Length == 0)
- {
- EditorGUILayout.HelpBox(
- "CSV 파일이 없습니다.\n" +
- "sync-from-notion.ps1을 먼저 실행하세요.",
- MessageType.Warning
- );
- }
- else
- {
- GUILayout.Label("발견된 CSV 파일:", EditorStyles.boldLabel);
- GUILayout.Space(5);
-
- foreach (var filePath in csvFiles)
- {
- string fileName = Path.GetFileNameWithoutExtension(filePath);
-
- GUILayout.BeginHorizontal();
-
- GUILayout.Label($"📊 {fileName}", GUILayout.Width(200));
-
- if (GUILayout.Button("Import", GUILayout.Width(100)))
- {
- ImportSingle(fileName);
- }
-
- GUILayout.EndHorizontal();
- }
- }
- }
-
- GUILayout.EndScrollView();
-
- GUILayout.Space(20);
-
- GUI.backgroundColor = Color.green;
- if (GUILayout.Button("Import All Data", GUILayout.Height(50)))
- {
- if (EditorUtility.DisplayDialog(
- "전체 데이터 Import",
- "모든 CSV 파일을 읽어서 ScriptableObject를 생성합니다.\n" +
- "기존 파일은 덮어씌워집니다.",
- "Import All",
- "Cancel"))
- {
- CSVToSOImporter.ImportAll();
- }
- }
- GUI.backgroundColor = Color.white;
-
- GUILayout.Space(10);
-
- EditorGUILayout.HelpBox(
- "Import 후 Assets/Data/ScriptableObjects 폴더를 확인하세요.",
- MessageType.None
- );
- }
-
- private void ImportSingle(string schemaName)
- {
- if (EditorUtility.DisplayDialog(
- $"{schemaName} Import",
- $"{schemaName}.csv를 읽어서 ScriptableObject를 생성합니다.\n" +
- "기존 파일은 덮어씌워집니다.",
- "Import",
- "Cancel"))
- {
- CSVToSOImporter.ImportSchema(schemaName);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Assets/Editor/DataImporter/ImporterWindow.cs.meta b/Assets/Editor/DataImporter/ImporterWindow.cs.meta
deleted file mode 100644
index 5fc7a11..0000000
--- a/Assets/Editor/DataImporter/ImporterWindow.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 017c665c9c855fa43b55f1d61c238903
\ No newline at end of file