12 KiB (Stored with Git LFS)
12 KiB (Stored with Git LFS)
SystemController 통합 가이드
개요
StreamDeck 단일 기능 버튼들을 통합 관리하는 시스템입니다. 각 기능은 하나의 버튼으로 매핑되어 간단하고 직관적으로 사용할 수 있습니다.
시스템 구조
1. Unity 측 (SystemController.cs)
경로: Assets/Scripts/Streamdeck/SystemController.cs
단일 기능 명령어들을 처리하는 통합 컨트롤러입니다.
주요 메서드:
ExecuteCommand(string command, Dictionary<string, object> parameters)- 명령어 실행 진입점
현재 구현된 기능:
OptiTrack 마커 토글
// 명령어: toggle_optitrack_markers, show_optitrack_markers, hide_optitrack_markers
public void ToggleOptitrackMarkers()
public void ShowOptitrackMarkers()
public void HideOptitrackMarkers()
2. Unity 서버 (StreamDeckServerManager.cs)
경로: Assets/Scripts/Streamdeck/StreamDeckServerManager.cs
WebSocket 서버에서 SystemController 명령을 라우팅합니다.
통합 코드:
// Line 19
public SystemController systemController { get; private set; }
// Line 64-69
systemController = FindObjectOfType<SystemController>();
if (systemController == null)
{
Debug.LogWarning("[StreamDeckServerManager] SystemController를 찾을 수 없습니다.");
}
// Line 347-351 (메시지 처리)
case "toggle_optitrack_markers":
case "show_optitrack_markers":
case "hide_optitrack_markers":
HandleSystemCommand(message);
break;
// Line 979-1014 (핸들러)
private void HandleSystemCommand(Dictionary<string, object> message)
{
string messageType = message.ContainsKey("type") ? message["type"].ToString() : null;
if (systemController == null)
{
Debug.LogError("[StreamDeckServerManager] SystemController가 null입니다!");
return;
}
// 파라미터 추출
Dictionary<string, object> parameters = new Dictionary<string, object>();
if (message.ContainsKey("data"))
{
var dataObject = message["data"];
if (dataObject is Newtonsoft.Json.Linq.JObject jObject)
{
foreach (var prop in jObject.Properties())
{
parameters[prop.Name] = prop.Value.ToString();
}
}
}
// SystemController의 ExecuteCommand 호출
systemController.ExecuteCommand(messageType, parameters);
}
3. StreamDeck 플러그인 (manifest.json)
경로: Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json
새로운 액션 정의 (Line 115-137):
{
"Icon": "images/optitrack_marker_icon.png",
"Name": "OptiTrack Marker Toggle",
"DisableAutomaticStates": false,
"States": [
{
"Image": "images/optitrack_marker_icon.png",
"TitleAlignment": "bottom",
"FontSize": "12"
},
{
"Image": "images/optitrack_marker_icon_off.png",
"TitleAlignment": "bottom",
"FontSize": "12"
}
],
"Settings": {},
"Controllers": ["Keypad"],
"UserTitleEnabled": true,
"SupportedInMultiActions": true,
"Tooltip": "Toggle OptiTrack markers visibility",
"UUID": "com.mirabox.streamingle.optitrack_marker_toggle"
}
4. StreamDeck 플러그인 로직 (plugin/index.js)
경로: Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js
액션 타입 등록 (Line 94-100):
else if (jsonObj.action === 'com.mirabox.streamingle.optitrack_marker_toggle') {
settings.actionType = 'optitrack_marker_toggle';
console.log('🎯 OptiTrack 마커 토글 등록:', jsonObj.context);
// 기본 제목 설정
setButtonTitle(jsonObj.context, '마커\nON');
}
버튼 클릭 처리 (Line 321-323):
case 'optitrack_marker_toggle':
handleOptitrackMarkerToggle(context);
break;
마커 토글 핸들러 (Line 489-513):
function handleOptitrackMarkerToggle(context) {
console.log('🎯 OptiTrack 마커 토글 실행');
// Unity에 마커 토글 요청
const message = JSON.stringify({
type: 'toggle_optitrack_markers'
});
console.log('📤 Unity에 OptiTrack 마커 토글 요청 전송:', message);
console.log('🔍 Unity 연결 상태:', isUnityConnected);
console.log('🔍 Unity 소켓 상태:', !!unitySocket);
if (unitySocket && unitySocket.readyState === WebSocket.OPEN) {
unitySocket.send(message);
console.log('✅ 메시지 전송 완료');
// 버튼 상태 토글 (0 <-> 1)
toggleButtonState(context);
} else {
console.error('❌ Unity 소켓이 연결되지 않음');
console.log('🔄 Unity 재연결 시도...');
connectToUnity();
}
}
버튼 상태 토글 (Line 515-543):
function toggleButtonState(context) {
const settings = getCurrentSettings(context);
const currentState = settings.currentState || 0;
const newState = currentState === 0 ? 1 : 0;
// 설정 업데이트
settings.currentState = newState;
setCurrentSettings(context, settings);
// 버튼 상태 변경
const stateMessage = {
event: 'setState',
context: context,
payload: {
state: newState,
target: 0 // hardware and software
}
};
if (websocket) {
websocket.send(JSON.stringify(stateMessage));
console.log('🎨 버튼 상태 업데이트:', newState === 0 ? 'ON' : 'OFF');
}
// 제목도 함께 변경
const title = newState === 0 ? '마커\nON' : '마커\nOFF';
setButtonTitle(context, title);
}
통신 프로토콜
WebSocket 메시지 형식
StreamDeck → Unity
{
"type": "toggle_optitrack_markers"
}
Unity 로그 (정상 동작 시)
[StreamDeckServerManager] 시스템 명령어 실행: toggle_optitrack_markers
[SystemController] 명령어 실행: toggle_optitrack_markers
[SystemController] OptiTrack 마커 표시: True/False
새 기능 추가 방법
1단계: SystemController에 기능 추가
// Assets/Scripts/Streamdeck/SystemController.cs
#region 새로운 기능 그룹
/// <summary>
/// 새로운 기능 설명
/// </summary>
public void NewFeatureMethod()
{
// 기능 구현
Log("새 기능 실행됨");
}
#endregion
2단계: ExecuteCommand에 case 추가
switch (command)
{
// 기존 명령어들...
case "new_feature_command":
NewFeatureMethod();
break;
}
3단계: StreamDeckServerManager에 메시지 타입 추가
// ProcessMessage의 switch문에 추가
case "new_feature_command":
HandleSystemCommand(message);
break;
4단계: manifest.json에 액션 추가
{
"Icon": "images/new_feature_icon.png",
"Name": "New Feature",
"DisableAutomaticStates": false,
"States": [
{
"Image": "images/new_feature_icon.png",
"TitleAlignment": "bottom",
"FontSize": "12"
}
],
"Settings": {},
"Controllers": ["Keypad"],
"UserTitleEnabled": true,
"SupportedInMultiActions": true,
"Tooltip": "New feature description",
"UUID": "com.mirabox.streamingle.new_feature"
}
5단계: plugin/index.js에 처리 로직 추가
액션 등록:
else if (jsonObj.action === 'com.mirabox.streamingle.new_feature') {
settings.actionType = 'new_feature';
console.log('🎯 새 기능 등록:', jsonObj.context);
}
클릭 처리:
case 'new_feature':
handleNewFeature(context);
break;
핸들러 함수:
function handleNewFeature(context) {
console.log('🎯 새 기능 실행');
const message = JSON.stringify({
type: 'new_feature_command'
});
if (unitySocket && unitySocket.readyState === WebSocket.OPEN) {
unitySocket.send(message);
console.log('✅ 메시지 전송 완료');
}
}
6단계: 배포
cd Streamdeck
deploy-plugin.bat
디버깅 방법
Unity 측 디버깅
Unity Console에서 로그 확인:
[SystemController] 명령어 실행: <command>
StreamDeck 플러그인 디버깅
방법 1: 개발자 도구 (Electron 기반)
- StreamDock 소프트웨어에서 F12 또는 Ctrl+Shift+I 시도
- Console 탭에서 JavaScript 로그 확인
방법 2: 로그 파일
- 경로:
%APPDATA%\Hotspot\StreamDock\logs\ - 최신 로그 파일 확인
방법 3: Unity 로그만으로 확인
- 버튼 클릭 시 Unity Console에 예상되는 로그가 나오는지 확인
- 잘못된 메시지가 오면 플러그인 설정 문제
트러블슈팅
문제: 버튼을 눌러도 Unity에 메시지가 안 옴
해결:
- Unity에서 SystemController GameObject가 씬에 있는지 확인
- StreamDeckServerManager가 SystemController를 찾았는지 로그 확인
- Unity WebSocket 서버가 포트 10701에서 실행 중인지 확인
문제: 잘못된 메시지 타입이 옴 (예: switch_camera)
해결:
- StreamDeck에서 올바른 액션을 추가했는지 확인
- 기존 버튼이 아닌 새 버튼에 "OptiTrack Marker Toggle" 액션을 추가
deploy-plugin.bat를 실행하여 최신 플러그인 배포
문제: 버튼에 제목이 안 나옴
해결:
plugin/index.js의willAppear이벤트에서setButtonTitle호출 확인- 제목에
\n으로 줄바꿈 가능 (예:'마커\nON')
문제: 버튼 상태가 안 바뀜
해결:
manifest.json에서DisableAutomaticStates: false확인States배열에 2개 상태 정의 확인toggleButtonState함수에서setState메시지 전송 확인
파일 위치 정리
Unity 파일
Assets/Scripts/Streamdeck/
├── SystemController.cs # 단일 기능 통합 컨트롤러
├── StreamDeckServerManager.cs # WebSocket 서버 & 라우팅
└── StreamDeckService.cs # WebSocket 서비스 (기존)
StreamDeck 플러그인 파일
Streamdeck/com.mirabox.streamingle.sdPlugin/
├── manifest.json # 액션 정의
├── plugin/
│ └── index.js # 플러그인 로직
├── propertyInspector/ # 설정 UI (기존 액션용)
└── images/ # 아이콘 이미지
├── optitrack_marker_icon.png # 마커 ON 상태
└── optitrack_marker_icon_off.png # 마커 OFF 상태
배포 스크립트
Streamdeck/
├── deploy-plugin.bat # 간편 실행용 배치 파일
├── deploy-plugin.ps1 # 실제 배포 스크립트
└── DEPLOY_README.md # 배포 가이드
현재 구현된 기능 목록
1. OptiTrack 마커 토글
- 명령어:
toggle_optitrack_markers,show_optitrack_markers,hide_optitrack_markers - UUID:
com.mirabox.streamingle.optitrack_marker_toggle - 설명: OptiTrack 마커 표시를 켜거나 끔
- 버튼 상태: 2개 (ON/OFF)
- 제목: "마커\nON" ↔ "마커\nOFF"
참고 자료
StreamDock 공식 문서
- 메인: https://creator.key123.vip/
- Getting Started: https://creator.key123.vip/en/guide/get-started.html
- Plugin SDK: https://creator.key123.vip/en/streamdock/plugin-sdk.html
StreamDock SDK (GitHub)
관련 문서
Implementation_Guide.md- Streamingle 플러그인 구현 가이드JSON_Protocol_Specification.md- JSON 통신 프로토콜 명세
작성일: 2025-01-27 버전: 1.0 작성자: SystemController 통합 프로젝트