using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Networking; /// /// CSV 데이터를 다양한 소스에서 Pull 방식으로 로드할 수 있는 클래스입니다. /// 호출 측에서 MoveNext()를 통해 처리 타이밍을 제어하세요. /// public static class CSVLoader { /// CSV 파싱 로직 정의 public interface IParser { T Parse(string[] columns); } /// 로딩 진행 상태 메타데이터 public struct Progress { public int TotalRows; public int RowsLoaded; } /// URL(HTTP)로부터 다운로드하여 파싱 public static IEnumerator Download(string url, IParser parser, List outList) { using var uwr = UnityWebRequest.Get(url); var operation = uwr.SendWebRequest(); // 요청이 완료될 때까지 대기 (Progress 타입 반환은 요청 상태 표시용) while (!operation.isDone) { yield return new Progress { TotalRows = 0, RowsLoaded = 0 }; } if (uwr.result != UnityWebRequest.Result.Success) { Logger.LogError($"CSVLoader: Download failed '{url}': {uwr.error}"); yield break; } string text = uwr.downloadHandler.text; foreach (var prog in ProcessText(text, parser, outList)) yield return prog; } /// 절대 경로의 파일을 읽어 파싱 public static IEnumerator LoadFromFile(string filePath, IParser parser, List outList) { if (!File.Exists(filePath)) { Logger.LogError($"CSVLoader: File not found '{filePath}'"); yield break; } string text = File.ReadAllText(filePath); foreach (var prog in ProcessText(text, parser, outList)) yield return prog; } /// StreamingAssets 내 상대 경로 또는 URL로부터 로드 public static IEnumerator LoadFromStreamingAssets(string relativePath, IParser parser, List outList) { string fullPath = Path.Combine(Application.streamingAssetsPath, relativePath); if (fullPath.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { var downloadEnum = Download(fullPath, parser, outList); while (downloadEnum.MoveNext()) yield return downloadEnum.Current; } else { var fileEnum = LoadFromFile(fullPath, parser, outList); while (fileEnum.MoveNext()) yield return fileEnum.Current; } } /// Resources 폴더 내 TextAsset로부터 로드 public static IEnumerator LoadFromResources(string resourcePath, IParser parser, List outList) { var ta = Resources.Load(resourcePath); if (ta == null) { Logger.LogError($"CSVLoader: Resource not found '{resourcePath}'"); yield break; } string text = ta.text; foreach (var prog in ProcessText(text, parser, outList)) yield return prog; } /// 텍스트를 실제 파싱하여 Progress를 반환하는 내부 모듈 static IEnumerable ProcessText(string text, IParser parser, List outList) { var lines = text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); int total = lines.Length; // 첫 번째 줄은 헤더이므로 무시 // 두 번째 줄은 설명이므로 무시 for (int i = 2; i < total; i++) { var cols = lines[i].TrimEnd('\r').Split(','); outList.Add(parser.Parse(cols)); yield return new Progress { TotalRows = total, RowsLoaded = i + 1 }; } } }