user 41270a34f5 Refactor: 전체 에디터 UXML 전환 + 대시보드/런타임 UI + 한글화 + NanumGothic 폰트
- 모든 컨트롤러 에디터를 IMGUI → UI Toolkit(UXML/USS)으로 전환
  (Camera, Item, Event, Avatar, System, StreamDeck, OptiTrack, Facial)
- StreamingleCommon.uss 공통 테마 + 개별 에디터 USS 스타일시트
- SystemController 서브매니저 분리 (OptiTrack, Facial, Recording, Screenshot 등)
- 런타임 컨트롤 패널 (ESC 토글, 좌측 오버레이, 150% 스케일)
- 웹 대시보드 서버 (StreamingleDashboardServer) + 리타게팅 통합
- 설정 도구(StreamingleControllerSetupTool) UXML 재작성 + 원클릭 설정
- SimplePoseTransfer UXML 에디터 추가
- 전체 UXML 한글화 + NanumGothic 폰트 적용
- Streamingle.Debug → Streamingle.Debugging 네임스페이스 변경 (Debug.Log 충돌 해결)
- 불필요 코드 제거 (rawkey.cs, RetargetingHTTPServer, OptitrackSkeletonAnimator 등)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 02:51:43 +09:00

363 lines
9.7 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using Streamingle;
public class ItemController : MonoBehaviour, IController
{
#region Classes
[System.Serializable]
public class ItemGroup
{
[Header("Group Settings")]
public string groupName = "New Item Group";
public GameObject[] itemObjects = new GameObject[0];
public ItemGroup(string name)
{
groupName = name;
}
public void SetActive(bool active)
{
if (itemObjects == null) return;
foreach (var obj in itemObjects)
{
if (obj != null)
{
obj.SetActive(active);
}
}
}
public bool IsActive()
{
if (itemObjects == null || itemObjects.Length == 0) return false;
// 모든 오브젝트가 활성화되어 있으면 true
return itemObjects.All(obj => obj != null && obj.activeSelf);
}
public override string ToString() => groupName;
}
#endregion
#region Events
public delegate void ItemGroupChangedEventHandler(ItemGroup oldGroup, ItemGroup newGroup);
public event ItemGroupChangedEventHandler OnItemGroupChanged;
#endregion
#region Fields
[SerializeField] public List<ItemGroup> itemGroups = new List<ItemGroup>();
[Header("Item Control Settings")]
[SerializeField] private bool autoFindGroups = true;
[SerializeField] private string groupTag = "ItemGroup";
private ItemGroup currentGroup;
private StreamDeckServerManager streamDeckManager;
#endregion
#region Properties
public ItemGroup CurrentGroup => currentGroup;
public int CurrentIndex => itemGroups.IndexOf(currentGroup);
#endregion
#region Unity Messages
private void Awake()
{
InitializeItemGroups();
// StreamDeckServerManager 찾기
streamDeckManager = FindObjectOfType<StreamDeckServerManager>();
if (streamDeckManager == null)
{
Debug.LogWarning("[ItemController] StreamDeckServerManager를 찾을 수 없습니다. 스트림덱 연동이 비활성화됩니다.");
}
}
#endregion
#region Initialization
public void InitializeItemGroups()
{
if (itemGroups == null)
{
itemGroups = new List<ItemGroup>();
}
if (autoFindGroups && itemGroups.Count == 0)
{
// "ItemGroup" 태그가 붙은 모든 오브젝트를 찾아서 그룹으로 등록
var groupObjects = GameObject.FindGameObjectsWithTag(groupTag);
foreach (var groupObj in groupObjects)
{
var group = new ItemGroup(groupObj.name);
group.itemObjects = new GameObject[] { groupObj };
itemGroups.Add(group);
}
Debug.Log($"[ItemController] {itemGroups.Count}개의 아이템 그룹을 자동으로 찾았습니다.");
}
// 유효하지 않은 그룹 제거
itemGroups.RemoveAll(group => group == null || group.itemObjects == null || group.itemObjects.Length == 0);
Debug.Log($"[ItemController] 총 {itemGroups.Count}개의 아이템 그룹이 등록되었습니다.");
}
#endregion
#region Public Methods
public void Set(int index)
{
if (index < 0 || index >= itemGroups.Count)
{
Debug.LogWarning($"[ItemController] 잘못된 인덱스: {index}, 유효 범위: 0-{itemGroups.Count - 1}");
return;
}
var oldGroup = currentGroup;
currentGroup = itemGroups[index];
// 모든 그룹 비활성화 후 현재 그룹만 활성화
foreach (var group in itemGroups)
{
group.SetActive(false);
}
currentGroup.SetActive(true);
// 이벤트 발생
OnItemGroupChanged?.Invoke(oldGroup, currentGroup);
// StreamDeck에 알림
NotifyItemChanged();
Debug.Log($"[ItemController] 그룹 변경: {currentGroup.groupName} (인덱스: {index})");
}
public void ToggleGroup(int index)
{
if (index < 0 || index >= itemGroups.Count)
{
Debug.LogWarning($"[ItemController] 잘못된 인덱스: {index}, 유효 범위: 0-{itemGroups.Count - 1}");
return;
}
var group = itemGroups[index];
bool newState = !group.IsActive();
group.SetActive(newState);
// StreamDeck에 알림
NotifyItemChanged();
Debug.Log($"[ItemController] 그룹 토글: {group.groupName} -> {(newState ? "" : "")}");
}
public void ToggleGroup(ItemGroup group)
{
if (group == null) return;
int index = itemGroups.IndexOf(group);
if (index >= 0)
{
ToggleGroup(index);
}
}
public void ToggleCurrentGroup()
{
if (currentGroup != null)
{
ToggleGroup(CurrentIndex);
}
}
public void ActivateAllGroups()
{
foreach (var group in itemGroups)
{
group.SetActive(true);
}
NotifyItemChanged();
Debug.Log("[ItemController] 모든 그룹 활성화");
}
public void DeactivateAllGroups()
{
foreach (var group in itemGroups)
{
group.SetActive(false);
}
NotifyItemChanged();
Debug.Log("[ItemController] 모든 그룹 비활성화");
}
public void AddGroup(string groupName, GameObject[] objects = null)
{
var newGroup = new ItemGroup(groupName);
if (objects != null)
{
newGroup.itemObjects = objects;
}
itemGroups.Add(newGroup);
NotifyItemChanged();
Debug.Log($"[ItemController] 그룹 추가: {groupName}");
}
public void RemoveGroup(int index)
{
if (index < 0 || index >= itemGroups.Count) return;
var removedGroup = itemGroups[index];
itemGroups.RemoveAt(index);
// 현재 그룹이 제거된 경우 첫 번째 그룹으로 변경
if (removedGroup == currentGroup)
{
currentGroup = itemGroups.Count > 0 ? itemGroups[0] : null;
}
NotifyItemChanged();
Debug.Log($"[ItemController] 그룹 제거: {removedGroup.groupName}");
}
#endregion
#region StreamDeck Integration
private void NotifyItemChanged()
{
if (streamDeckManager != null)
{
var updateMessage = new
{
type = "item_changed",
timestamp = System.DateTime.UtcNow.ToString("o"),
version = "1.0",
data = new
{
item_data = GetItemListData(),
current_item = GetCurrentItemState()
}
};
string json = JsonUtility.ToJson(updateMessage);
streamDeckManager.BroadcastMessage(json);
Debug.Log("[ItemController] 아이템 변경 알림 전송됨");
}
}
public ItemListData GetItemListData()
{
return new ItemListData
{
item_count = itemGroups.Count,
items = itemGroups.Select((g, i) => new ItemPresetData
{
index = i,
name = g.groupName,
isActive = g.IsActive()
}).ToArray(),
current_index = CurrentIndex
};
}
public ItemStateData GetCurrentItemState()
{
if (currentGroup == null) return null;
return new ItemStateData
{
current_index = CurrentIndex,
item_name = currentGroup.groupName,
isActive = currentGroup.IsActive(),
total_items = itemGroups.Count
};
}
public string GetItemListJson()
{
return JsonUtility.ToJson(GetItemListData());
}
public string GetItemStateJson()
{
return JsonUtility.ToJson(GetCurrentItemState());
}
#endregion
#region Data Classes
[System.Serializable]
public class ItemPresetData
{
public int index;
public string name;
public bool isActive;
}
[System.Serializable]
public class ItemListData
{
public int item_count;
public ItemPresetData[] items;
public int current_index;
}
[System.Serializable]
public class ItemStateData
{
public int current_index;
public string item_name;
public bool isActive;
public int total_items;
}
#endregion
#region IController Implementation
public string GetControllerId()
{
return "item_controller";
}
public string GetControllerName()
{
return "Item Controller";
}
public object GetControllerData()
{
return GetItemListData();
}
public void ExecuteAction(string actionId, object parameters)
{
switch (actionId)
{
case "toggle_item":
{
if (parameters is int toggleIndex)
{
ToggleGroup(toggleIndex);
}
}
break;
case "set_item":
{
if (parameters is int setIndex)
{
Set(setIndex);
}
}
break;
case "activate_all":
ActivateAllGroups();
break;
case "deactivate_all":
DeactivateAllGroups();
break;
default:
Debug.LogWarning($"[ItemController] 알 수 없는 액션: {actionId}");
break;
}
}
#endregion
}