using UnityEngine; using System; using System.Collections.Generic; using System.Linq; using Streamingle; using Newtonsoft.Json; public class AvatarOutfitController : MonoBehaviour, IController { #region Classes [System.Serializable] public class AvatarData { [Header("Avatar Settings")] public string avatarName = "New Avatar"; public GameObject avatarObject; [Header("Outfit Settings")] public OutfitData[] outfits = new OutfitData[0]; // 현재 착용 중인 의상 인덱스 [SerializeField] private int currentOutfitIndex = 0; public int CurrentOutfitIndex => currentOutfitIndex; public OutfitData CurrentOutfit => outfits != null && outfits.Length > currentOutfitIndex ? outfits[currentOutfitIndex] : null; public AvatarData(string name) { avatarName = name; outfits = new OutfitData[0]; } public void SetOutfit(int outfitIndex) { if (outfits == null || outfitIndex < 0 || outfitIndex >= outfits.Length) { Debug.LogWarning($"[AvatarData] 잘못된 의상 인덱스: {outfitIndex}"); return; } // 현재 의상 제거 if (CurrentOutfit != null) { CurrentOutfit.RemoveOutfit(); } // 새 의상 적용 currentOutfitIndex = outfitIndex; if (CurrentOutfit != null) { CurrentOutfit.ApplyOutfit(); Debug.Log($"[AvatarData] {avatarName} 의상 변경: {CurrentOutfit.outfitName}"); } } public override string ToString() => avatarName; } [System.Serializable] public class OutfitData { [Header("Outfit Info")] public string outfitName = "New Outfit"; [Header("Clothing GameObjects")] public GameObject[] clothingObjects = new GameObject[0]; // 활성화할 의상 오브젝트들 public GameObject[] hideObjects = new GameObject[0]; // 비활성화할 오브젝트들 (기본 의상 등) public void ApplyOutfit() { // 의상 오브젝트들 활성화 foreach (var obj in clothingObjects) { if (obj != null) obj.SetActive(true); } // 숨겨야 할 오브젝트들 비활성화 foreach (var obj in hideObjects) { if (obj != null) obj.SetActive(false); } } public void RemoveOutfit() { // 의상 오브젝트들 비활성화 foreach (var obj in clothingObjects) { if (obj != null) obj.SetActive(false); } // 숨겨진 오브젝트들 다시 활성화 foreach (var obj in hideObjects) { if (obj != null) obj.SetActive(true); } } } #endregion #region Events public delegate void AvatarOutfitChangedEventHandler(AvatarData avatar, OutfitData oldOutfit, OutfitData newOutfit); public event AvatarOutfitChangedEventHandler OnAvatarOutfitChanged; #endregion #region Fields [SerializeField] public List avatars = new List(); [Header("Avatar Control Settings")] [SerializeField] private bool autoFindAvatars = false; [SerializeField] private string avatarTag = "Avatar"; private AvatarData currentAvatar; private StreamDeckServerManager streamDeckManager; #endregion #region Properties public AvatarData CurrentAvatar => currentAvatar; public int CurrentAvatarIndex => avatars.IndexOf(currentAvatar); #endregion #region Unity Messages private void Awake() { InitializeAvatars(); // StreamDeckServerManager 찾기 streamDeckManager = FindObjectOfType(); if (streamDeckManager == null) { Debug.LogWarning("[AvatarOutfitController] StreamDeckServerManager를 찾을 수 없습니다. 스트림덱 연동이 비활성화됩니다."); } } #endregion #region Initialization public void InitializeAvatars() { if (avatars == null) { avatars = new List(); } if (autoFindAvatars && avatars.Count == 0) { // "Avatar" 태그가 붙은 모든 오브젝트를 찾아서 아바타로 등록 var avatarObjects = GameObject.FindGameObjectsWithTag(avatarTag); foreach (var avatarObj in avatarObjects) { var avatar = new AvatarData(avatarObj.name); avatar.avatarObject = avatarObj; avatars.Add(avatar); } Debug.Log($"[AvatarOutfitController] {avatars.Count}개의 아바타를 자동으로 찾았습니다."); } // 유효하지 않은 아바타 제거 avatars.RemoveAll(avatar => avatar == null || avatar.avatarObject == null); // 첫 번째 아바타를 기본으로 설정 if (avatars.Count > 0 && currentAvatar == null) { currentAvatar = avatars[0]; } Debug.Log($"[AvatarOutfitController] 총 {avatars.Count}개의 아바타가 등록되었습니다."); } #endregion #region Public Methods public void Set(int index) { // IController 인터페이스 구현 - 첫 번째 아바타의 특정 의상으로 설정 if (avatars.Count > 0) { SetAvatarOutfit(0, index); } else { Debug.LogWarning("[AvatarOutfitController] 설정할 아바타가 없습니다."); } } public void SetAvatarOutfit(int avatarIndex, int outfitIndex) { if (avatarIndex < 0 || avatarIndex >= avatars.Count) { Debug.LogWarning($"[AvatarOutfitController] 잘못된 아바타 인덱스: {avatarIndex}"); return; } var avatar = avatars[avatarIndex]; var oldOutfit = avatar.CurrentOutfit; avatar.SetOutfit(outfitIndex); // 이벤트 발생 OnAvatarOutfitChanged?.Invoke(avatar, oldOutfit, avatar.CurrentOutfit); // StreamDeck에 알림 NotifyAvatarOutfitChanged(avatar); Debug.Log($"[AvatarOutfitController] 아바타 의상 변경: {avatar.avatarName} -> {avatar.CurrentOutfit?.outfitName}"); } public void AddAvatar(string avatarName, GameObject avatarObject = null) { var newAvatar = new AvatarData(avatarName); if (avatarObject != null) { newAvatar.avatarObject = avatarObject; } avatars.Add(newAvatar); NotifyAvatarOutfitChanged(newAvatar); Debug.Log($"[AvatarOutfitController] 아바타 추가: {avatarName}"); } public void RemoveAvatar(int index) { if (index < 0 || index >= avatars.Count) return; var removedAvatar = avatars[index]; avatars.RemoveAt(index); // 현재 아바타가 제거된 경우 첫 번째 아바타로 변경 if (removedAvatar == currentAvatar) { currentAvatar = avatars.Count > 0 ? avatars[0] : null; } Debug.Log($"[AvatarOutfitController] 아바타 제거: {removedAvatar.avatarName}"); } #endregion #region StreamDeck Integration private void NotifyAvatarOutfitChanged(AvatarData avatar) { if (streamDeckManager != null) { streamDeckManager.NotifyAvatarOutfitChanged(); } } public AvatarOutfitListData GetAvatarOutfitListData() { return new AvatarOutfitListData { avatar_count = avatars.Count, avatars = avatars.Select((a, i) => new AvatarPresetData { index = i, name = a.avatarName, current_outfit_index = a.CurrentOutfitIndex, current_outfit_name = a.CurrentOutfit?.outfitName ?? "없음", outfits = a.outfits?.Select((o, oi) => new OutfitPresetData { index = oi, name = o.outfitName }).ToArray() ?? new OutfitPresetData[0], hotkey = "스트림덱 전용" }).ToArray(), current_avatar_index = CurrentAvatarIndex }; } public AvatarOutfitStateData GetCurrentAvatarOutfitState() { if (currentAvatar == null) return null; return new AvatarOutfitStateData { current_avatar_index = CurrentAvatarIndex, avatar_name = currentAvatar.avatarName, current_outfit_index = currentAvatar.CurrentOutfitIndex, current_outfit_name = currentAvatar.CurrentOutfit?.outfitName ?? "없음", total_avatars = avatars.Count }; } public string GetAvatarOutfitListJson() { return JsonConvert.SerializeObject(GetAvatarOutfitListData()); } public string GetAvatarOutfitStateJson() { return JsonConvert.SerializeObject(GetCurrentAvatarOutfitState()); } #endregion #region Data Classes [System.Serializable] public class OutfitPresetData { public int index; public string name; } [System.Serializable] public class AvatarPresetData { public int index; public string name; public int current_outfit_index; public string current_outfit_name; public OutfitPresetData[] outfits; public string hotkey; } [System.Serializable] public class AvatarOutfitListData { public int avatar_count; public AvatarPresetData[] avatars; public int current_avatar_index; } [System.Serializable] public class AvatarOutfitStateData { public int current_avatar_index; public string avatar_name; public int current_outfit_index; public string current_outfit_name; public int total_avatars; } #endregion #region IController Implementation public string GetControllerId() { return "avatar_outfit_controller"; } public string GetControllerName() { return "Avatar Outfit Controller"; } public object GetControllerData() { return GetAvatarOutfitListData(); } public void ExecuteAction(string actionId, object parameters) { switch (actionId) { case "set_avatar_outfit": if (parameters is Dictionary setParams) { if (setParams.ContainsKey("avatar_index") && setParams.ContainsKey("outfit_index")) { int avatarIndex = Convert.ToInt32(setParams["avatar_index"]); int outfitIndex = Convert.ToInt32(setParams["outfit_index"]); SetAvatarOutfit(avatarIndex, outfitIndex); } } break; default: Debug.LogWarning($"[AvatarOutfitController] 알 수 없는 액션: {actionId}"); break; } } #endregion }