277 lines
9.8 KiB
C#

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<string, GameObject> loadedAvatars = new Dictionary<string, GameObject>();
private StreamingleDebugWindow debugWindow;
[SerializeField]
public bool createDebugUI = false;
/// <summary>
/// 스트리밍글 아바타 파일을 비동기적으로 로드합니다.
/// </summary>
/// <param name="filePath">로드할 .streamingle 파일의 전체 경로</param>
/// <param name="parentTransform">로드된 아바타가 배치될 부모 Transform (null인 경우 월드 좌표계에 생성)</param>
/// <returns></returns>
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<GameObject>(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);
}
}
/// <summary>
/// 특정 아바타를 제거합니다.
/// </summary>
public void UnloadAvatar(string avatarKey)
{
if (loadedAvatars.ContainsKey(avatarKey))
{
Destroy(loadedAvatars[avatarKey]);
loadedAvatars.Remove(avatarKey);
UpdateDebugText($"아바타 언로드됨: {avatarKey}");
}
}
/// <summary>
/// 모든 아바타를 제거합니다.
/// </summary>
public void UnloadAllAvatars()
{
foreach (var avatar in loadedAvatars.Values)
{
if (avatar != null)
{
Destroy(avatar);
}
}
loadedAvatars.Clear();
UpdateDebugText("모든 아바타가 언로드됨");
}
/// <summary>
/// 특정 아바타 게임오브젝트를 반환합니다.
/// </summary>
public GameObject GetLoadedAvatar(string avatarKey)
{
return loadedAvatars.ContainsKey(avatarKey) ? loadedAvatars[avatarKey] : null;
}
/// <summary>
/// 현재 로드된 모든 아바타의 목록을 반환합니다.
/// </summary>
public Dictionary<string, GameObject> GetLoadedAvatars()
{
return new Dictionary<string, GameObject>(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
}
/// <summary>
/// 파일 경로를 통해 아바타를 직접 로드합니다.
/// </summary>
public void LoadAvatarFromPath(string path)
{
if (!string.IsNullOrEmpty(path))
{
StartCoroutine(LoadAvatarAsync(path));
}
}
}
// 사용 편의성을 위한 정적 클래스
public static class StreamingleAvatarLoader
{
/// <summary>
/// 간단한 아바타 로드를 위한 정적 메서드
/// </summary>
public static StreamingleAvatarImport CreateLoader(bool withDebugUI = false)
{
GameObject loaderObject = new GameObject("StreamingleAvatarLoader");
var loader = loaderObject.AddComponent<StreamingleAvatarImport>();
loader.createDebugUI = withDebugUI;
return loader;
}
/// <summary>
/// 파일 브라우저를 통해 아바타를 로드합니다.
/// </summary>
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
}
}
}