562 lines
15 KiB
C#
562 lines
15 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Streamingle;
|
|
using UnityRawInput;
|
|
|
|
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];
|
|
|
|
[Header("Hotkey Settings")]
|
|
public List<RawKey> hotkeys = new List<RawKey>();
|
|
[System.NonSerialized] public bool isRecording = false;
|
|
[System.NonSerialized] private List<KeyCode> unityKeys = new List<KeyCode>();
|
|
[System.NonSerialized] private float recordStartTime;
|
|
[System.NonSerialized] private const float MAX_RECORD_TIME = 2f;
|
|
|
|
public ItemGroup(string name)
|
|
{
|
|
groupName = name;
|
|
hotkeys = new List<RawKey>();
|
|
}
|
|
|
|
public void StartRecording()
|
|
{
|
|
isRecording = true;
|
|
recordStartTime = Time.time;
|
|
hotkeys.Clear();
|
|
}
|
|
|
|
public void StopRecording()
|
|
{
|
|
isRecording = false;
|
|
InitializeUnityKeys();
|
|
}
|
|
|
|
public void UpdateRecording()
|
|
{
|
|
if (!isRecording) return;
|
|
|
|
if (Time.time - recordStartTime > MAX_RECORD_TIME)
|
|
{
|
|
StopRecording();
|
|
return;
|
|
}
|
|
|
|
foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode)))
|
|
{
|
|
if (Input.GetKeyDown(keyCode) && KeyMapping.TryGetRawKey(keyCode, out RawKey rawKey))
|
|
{
|
|
if (!hotkeys.Contains(rawKey))
|
|
{
|
|
hotkeys.Add(rawKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool allKeysReleased = hotkeys.Any() && hotkeys.All(key => !Input.GetKey(KeyMapping.TryGetKeyCode(key, out KeyCode keyCode) ? keyCode : KeyCode.None));
|
|
|
|
if (allKeysReleased)
|
|
{
|
|
StopRecording();
|
|
}
|
|
}
|
|
|
|
public void InitializeUnityKeys()
|
|
{
|
|
unityKeys.Clear();
|
|
|
|
if (hotkeys == null || !hotkeys.Any()) return;
|
|
|
|
foreach (var hotkey in hotkeys)
|
|
{
|
|
if (KeyMapping.TryGetKeyCode(hotkey, out KeyCode keyCode) && keyCode != KeyCode.None)
|
|
{
|
|
unityKeys.Add(keyCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsTriggered()
|
|
{
|
|
if (isRecording) return false;
|
|
|
|
if (hotkeys == null || !hotkeys.Any()) return false;
|
|
|
|
bool allHotkeysPressed = hotkeys.All(key => RawInput.IsKeyDown(key));
|
|
if (allHotkeysPressed) return true;
|
|
|
|
if (unityKeys.Any())
|
|
{
|
|
return unityKeys.All(key => Input.GetKey(key));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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() =>
|
|
hotkeys?.Any() == true ? $"{groupName} ({string.Join(" + ", hotkeys)})" : $"{groupName} (설정되지 않음)";
|
|
}
|
|
|
|
private static class KeyMapping
|
|
{
|
|
private static readonly Dictionary<KeyCode, RawKey> _mapping;
|
|
|
|
static KeyMapping()
|
|
{
|
|
_mapping = new Dictionary<KeyCode, RawKey>(RawKeySetup.KeyMapping);
|
|
}
|
|
|
|
public static bool TryGetRawKey(KeyCode keyCode, out RawKey rawKey)
|
|
{
|
|
return _mapping.TryGetValue(keyCode, out rawKey);
|
|
}
|
|
|
|
public static bool TryGetKeyCode(RawKey rawKey, out KeyCode keyCode)
|
|
{
|
|
var pair = _mapping.FirstOrDefault(x => x.Value == rawKey);
|
|
keyCode = pair.Key;
|
|
return keyCode != KeyCode.None;
|
|
}
|
|
|
|
public static bool IsValidRawKey(RawKey key)
|
|
{
|
|
return _mapping.ContainsValue(key);
|
|
}
|
|
}
|
|
#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();
|
|
InitializeRawInput();
|
|
|
|
// StreamDeckServerManager 찾기
|
|
streamDeckManager = FindObjectOfType<StreamDeckServerManager>();
|
|
if (streamDeckManager == null)
|
|
{
|
|
Debug.LogWarning("[ItemController] StreamDeckServerManager를 찾을 수 없습니다. 스트림덱 연동이 비활성화됩니다.");
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (RawInput.IsRunning)
|
|
{
|
|
RawInput.OnKeyDown -= HandleRawKeyDown;
|
|
RawInput.Stop();
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
UpdateHotkeyRecording();
|
|
HandleHotkeys();
|
|
}
|
|
#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}개의 아이템 그룹이 등록되었습니다.");
|
|
}
|
|
|
|
private void InitializeRawInput()
|
|
{
|
|
if (!RawInput.IsRunning)
|
|
{
|
|
RawInput.Start();
|
|
RawInput.WorkInBackground = true;
|
|
}
|
|
RawInput.OnKeyDown += HandleRawKeyDown;
|
|
}
|
|
#endregion
|
|
|
|
#region Hotkey Management
|
|
private void UpdateHotkeyRecording()
|
|
{
|
|
foreach (var group in itemGroups)
|
|
{
|
|
if (group?.isRecording == true)
|
|
{
|
|
group.UpdateRecording();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HandleRawKeyDown(RawKey key)
|
|
{
|
|
// 핫키 레코딩 중이면 무시
|
|
if (itemGroups.Any(g => g?.isRecording == true)) return;
|
|
}
|
|
|
|
private void HandleHotkeys()
|
|
{
|
|
foreach (var group in itemGroups)
|
|
{
|
|
if (group?.IsTriggered() == true)
|
|
{
|
|
ToggleGroup(group);
|
|
Debug.Log($"[ItemController] 핫키로 그룹 토글: {group.groupName}");
|
|
}
|
|
}
|
|
}
|
|
#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}");
|
|
}
|
|
|
|
public void StartRecordingHotkey(int groupIndex)
|
|
{
|
|
if (groupIndex < 0 || groupIndex >= itemGroups.Count) return;
|
|
|
|
var group = itemGroups[groupIndex];
|
|
group.StartRecording();
|
|
Debug.Log($"[ItemController] 핫키 레코딩 시작: {group.groupName}");
|
|
}
|
|
|
|
public void StopRecordingHotkey(int groupIndex)
|
|
{
|
|
if (groupIndex < 0 || groupIndex >= itemGroups.Count) return;
|
|
|
|
var group = itemGroups[groupIndex];
|
|
group.StopRecording();
|
|
Debug.Log($"[ItemController] 핫키 레코딩 완료: {group.groupName} -> {group}");
|
|
}
|
|
#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(),
|
|
hotkey = g.ToString()
|
|
}).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;
|
|
public string hotkey;
|
|
}
|
|
|
|
[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;
|
|
case "start_recording_hotkey":
|
|
if (parameters is int recordIndex)
|
|
{
|
|
StartRecordingHotkey(recordIndex);
|
|
}
|
|
break;
|
|
case "stop_recording_hotkey":
|
|
if (parameters is int stopIndex)
|
|
{
|
|
StopRecordingHotkey(stopIndex);
|
|
}
|
|
break;
|
|
default:
|
|
Debug.LogWarning($"[ItemController] 알 수 없는 액션: {actionId}");
|
|
break;
|
|
}
|
|
}
|
|
#endregion
|
|
} |