376 lines
11 KiB
C#
376 lines
11 KiB
C#
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<AvatarData> avatars = new List<AvatarData>();
|
|
|
|
[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<StreamDeckServerManager>();
|
|
if (streamDeckManager == null)
|
|
{
|
|
Debug.LogWarning("[AvatarOutfitController] StreamDeckServerManager를 찾을 수 없습니다. 스트림덱 연동이 비활성화됩니다.");
|
|
}
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Initialization
|
|
public void InitializeAvatars()
|
|
{
|
|
if (avatars == null)
|
|
{
|
|
avatars = new List<AvatarData>();
|
|
}
|
|
|
|
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<string, object> 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
|
|
} |