using UnityEngine; using System.IO; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine.UI; using UnityEngine.EventSystems; using Streamingle.Debug; using System.Linq; namespace Streamingle { public class StreamingleAvatarImport : MonoBehaviour { private Dictionary loadedAvatars = new Dictionary(); private StreamingleDebugWindow debugWindow; [SerializeField] public bool createDebugUI = false; /// /// 스트리밍글 아바타 파일을 비동기적으로 로드합니다. /// /// 로드할 .streamingle 파일의 전체 경로 /// 로드된 아바타가 배치될 부모 Transform (null인 경우 월드 좌표계에 생성) /// public IEnumerator LoadAvatarAsync(string filePath, Transform parentTransform = null) { if (!File.Exists(filePath)) { UpdateDebugText($"파일을 찾을 수 없음: {filePath}"); UnityEngine.Debug.LogError($"파일을 찾을 수 없습니다: {filePath}"); yield break; } string avatarKey = Path.GetFileNameWithoutExtension(filePath); UpdateDebugText($"아바타 로드 중: {avatarKey}"); if (loadedAvatars.ContainsKey(avatarKey)) { Destroy(loadedAvatars[avatarKey]); loadedAvatars.Remove(avatarKey); } AssetBundle avatarBundle = null; // AssetBundle 로드 AssetBundleCreateRequest bundleRequest = null; try { bundleRequest = AssetBundle.LoadFromFileAsync(filePath); } catch (System.Exception e) { UpdateDebugText($"AssetBundle 로드 실패: {e.Message}"); UnityEngine.Debug.LogError($"AssetBundle 로드 실패: {e.Message}"); yield break; } yield return bundleRequest; if (bundleRequest.assetBundle == null) { UpdateDebugText("AssetBundle 로드 실패"); UnityEngine.Debug.LogError("AssetBundle 로드 실패"); yield break; } avatarBundle = bundleRequest.assetBundle; // 아바타 프리팹 로드 AssetBundleRequest prefabRequest = null; try { string[] assetNames = avatarBundle.GetAllAssetNames(); UnityEngine.Debug.Log($"번들 내 에셋 목록: {string.Join(", ", assetNames)}"); // 번들 내 첫 번째 프리팹을 로드하거나, 특정 이름 패턴을 가진 프리팹을 찾아서 로드 string prefabPath = assetNames.FirstOrDefault(name => name.EndsWith(".prefab")); if (string.IsNullOrEmpty(prefabPath)) { UpdateDebugText("프리팹을 찾을 수 없습니다."); UnityEngine.Debug.LogError("번들 내에서 프리팹을 찾을 수 없습니다."); if (avatarBundle != null) avatarBundle.Unload(false); yield break; } prefabRequest = avatarBundle.LoadAssetAsync(prefabPath); if (prefabRequest == null) { UpdateDebugText("프리팹 로드 요청 생성 실패"); UnityEngine.Debug.LogError("프리팹 로드 요청을 생성할 수 없습니다."); if (avatarBundle != null) avatarBundle.Unload(false); yield break; } } catch (System.Exception e) { UpdateDebugText($"프리팹 로드 실패: {e.Message}"); UnityEngine.Debug.LogError($"프리팹 로드 실패: {e.Message}"); if (avatarBundle != null) avatarBundle.Unload(false); yield break; } yield return prefabRequest; if (prefabRequest.asset == null) { UpdateDebugText("아바타 프리팹 로드 실패"); UnityEngine.Debug.LogError("아바타 프리팹 로드 실패"); if (avatarBundle != null) avatarBundle.Unload(false); yield break; } try { // 아바타 인스턴스화 GameObject avatarPrefab = prefabRequest.asset as GameObject; GameObject avatar = Instantiate(avatarPrefab, parentTransform); avatar.name = avatarKey; loadedAvatars.Add(avatarKey, avatar); UpdateDebugText($"아바타 로드 완료: {avatarKey}"); } catch (System.Exception e) { UpdateDebugText($"아바타 생성 실패: {e.Message}"); UnityEngine.Debug.LogError($"아바타 생성 실패: {e.Message}"); } finally { if (avatarBundle != null) avatarBundle.Unload(false); } } /// /// 특정 아바타를 제거합니다. /// public void UnloadAvatar(string avatarKey) { if (loadedAvatars.ContainsKey(avatarKey)) { Destroy(loadedAvatars[avatarKey]); loadedAvatars.Remove(avatarKey); UpdateDebugText($"아바타 언로드됨: {avatarKey}"); } } /// /// 모든 아바타를 제거합니다. /// public void UnloadAllAvatars() { foreach (var avatar in loadedAvatars.Values) { if (avatar != null) { Destroy(avatar); } } loadedAvatars.Clear(); UpdateDebugText("모든 아바타가 언로드됨"); } /// /// 특정 아바타 게임오브젝트를 반환합니다. /// public GameObject GetLoadedAvatar(string avatarKey) { return loadedAvatars.ContainsKey(avatarKey) ? loadedAvatars[avatarKey] : null; } /// /// 현재 로드된 모든 아바타의 목록을 반환합니다. /// public Dictionary GetLoadedAvatars() { return new Dictionary(loadedAvatars); } private void Start() { if (createDebugUI) { debugWindow = StreamingleDebugWindow.Create(this, () => OpenFileBrowser()); } } private void OnDestroy() { if (debugWindow != null) { debugWindow.UnregisterImporter(this); } // 먼저 모든 아바타를 언로드 foreach (var avatar in loadedAvatars.Values) { if (avatar != null) { Destroy(avatar); } } loadedAvatars.Clear(); } private void UpdateDebugText(string status) { if (createDebugUI && debugWindow != null) { debugWindow.UpdateDebugStatus(); } } private void OpenFileBrowser() { #if UNITY_EDITOR string path = UnityEditor.EditorUtility.OpenFilePanel("아바타 로드", "", "streamingle"); if (!string.IsNullOrEmpty(path)) { StartCoroutine(LoadAvatarAsync(path)); } #else // 빌드된 게임에서는 StandaloneFileBrowser 등의 써드파티 에셋을 사용하거나 // 특정 폴더에서만 로드하도록 구현해야 합니다. UnityEngine.Debug.LogWarning("빌드된 게임에서는 파일 브라우저가 지원되지 않습니다."); #endif } /// /// 파일 경로를 통해 아바타를 직접 로드합니다. /// public void LoadAvatarFromPath(string path) { if (!string.IsNullOrEmpty(path)) { StartCoroutine(LoadAvatarAsync(path)); } } } // 사용 편의성을 위한 정적 클래스 public static class StreamingleAvatarLoader { /// /// 간단한 아바타 로드를 위한 정적 메서드 /// public static StreamingleAvatarImport CreateLoader(bool withDebugUI = false) { GameObject loaderObject = new GameObject("StreamingleAvatarLoader"); var loader = loaderObject.AddComponent(); loader.createDebugUI = withDebugUI; return loader; } /// /// 파일 브라우저를 통해 아바타를 로드합니다. /// public static void LoadAvatarWithFileBrowser(StreamingleAvatarImport loader) { #if UNITY_EDITOR string path = UnityEditor.EditorUtility.OpenFilePanel("아바타 로드", "", "streamingle"); if (!string.IsNullOrEmpty(path)) { loader.LoadAvatarFromPath(path); } #else UnityEngine.Debug.LogWarning("빌드된 게임에서는 파일 브라우저가 지원되지 않습니다."); #endif } } }