# SystemController 통합 가이드 ## 개요 StreamDeck 단일 기능 버튼들을 통합 관리하는 시스템입니다. 각 기능은 하나의 버튼으로 매핑되어 간단하고 직관적으로 사용할 수 있습니다. --- ## 시스템 구조 ### 1. Unity 측 (SystemController.cs) **경로**: `Assets/Scripts/Streamdeck/SystemController.cs` 단일 기능 명령어들을 처리하는 통합 컨트롤러입니다. **주요 메서드:** - `ExecuteCommand(string command, Dictionary parameters)` - 명령어 실행 진입점 **현재 구현된 기능:** #### OptiTrack 마커 토글 ```csharp // 명령어: 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 명령을 라우팅합니다. **통합 코드:** ```csharp // Line 19 public SystemController systemController { get; private set; } // Line 64-69 systemController = FindObjectOfType(); 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 message) { string messageType = message.ContainsKey("type") ? message["type"].ToString() : null; if (systemController == null) { Debug.LogError("[StreamDeckServerManager] SystemController가 null입니다!"); return; } // 파라미터 추출 Dictionary parameters = new Dictionary(); 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): ```json { "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): ```javascript 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): ```javascript case 'optitrack_marker_toggle': handleOptitrackMarkerToggle(context); break; ``` **마커 토글 핸들러** (Line 489-513): ```javascript 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): ```javascript 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** ```json { "type": "toggle_optitrack_markers" } ``` **Unity 로그 (정상 동작 시)** ``` [StreamDeckServerManager] 시스템 명령어 실행: toggle_optitrack_markers [SystemController] 명령어 실행: toggle_optitrack_markers [SystemController] OptiTrack 마커 표시: True/False ``` --- ## 새 기능 추가 방법 ### 1단계: SystemController에 기능 추가 ```csharp // Assets/Scripts/Streamdeck/SystemController.cs #region 새로운 기능 그룹 /// /// 새로운 기능 설명 /// public void NewFeatureMethod() { // 기능 구현 Log("새 기능 실행됨"); } #endregion ``` ### 2단계: ExecuteCommand에 case 추가 ```csharp switch (command) { // 기존 명령어들... case "new_feature_command": NewFeatureMethod(); break; } ``` ### 3단계: StreamDeckServerManager에 메시지 타입 추가 ```csharp // ProcessMessage의 switch문에 추가 case "new_feature_command": HandleSystemCommand(message); break; ``` ### 4단계: manifest.json에 액션 추가 ```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에 처리 로직 추가 **액션 등록:** ```javascript else if (jsonObj.action === 'com.mirabox.streamingle.new_feature') { settings.actionType = 'new_feature'; console.log('🎯 새 기능 등록:', jsonObj.context); } ``` **클릭 처리:** ```javascript case 'new_feature': handleNewFeature(context); break; ``` **핸들러 함수:** ```javascript 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단계: 배포 ```bash cd Streamdeck deploy-plugin.bat ``` --- ## 디버깅 방법 ### Unity 측 디버깅 Unity Console에서 로그 확인: ``` [SystemController] 명령어 실행: ``` ### StreamDeck 플러그인 디버깅 **방법 1: 개발자 도구 (Electron 기반)** - StreamDock 소프트웨어에서 **F12** 또는 **Ctrl+Shift+I** 시도 - Console 탭에서 JavaScript 로그 확인 **방법 2: 로그 파일** - 경로: `%APPDATA%\Hotspot\StreamDock\logs\` - 최신 로그 파일 확인 **방법 3: Unity 로그만으로 확인** - 버튼 클릭 시 Unity Console에 예상되는 로그가 나오는지 확인 - 잘못된 메시지가 오면 플러그인 설정 문제 --- ## 트러블슈팅 ### 문제: 버튼을 눌러도 Unity에 메시지가 안 옴 **해결:** 1. Unity에서 SystemController GameObject가 씬에 있는지 확인 2. StreamDeckServerManager가 SystemController를 찾았는지 로그 확인 3. Unity WebSocket 서버가 포트 10701에서 실행 중인지 확인 ### 문제: 잘못된 메시지 타입이 옴 (예: switch_camera) **해결:** 1. StreamDeck에서 올바른 액션을 추가했는지 확인 2. 기존 버튼이 아닌 새 버튼에 "OptiTrack Marker Toggle" 액션을 추가 3. `deploy-plugin.bat`를 실행하여 최신 플러그인 배포 ### 문제: 버튼에 제목이 안 나옴 **해결:** 1. `plugin/index.js`의 `willAppear` 이벤트에서 `setButtonTitle` 호출 확인 2. 제목에 `\n`으로 줄바꿈 가능 (예: `'마커\nON'`) ### 문제: 버튼 상태가 안 바뀜 **해결:** 1. `manifest.json`에서 `DisableAutomaticStates: false` 확인 2. `States` 배열에 2개 상태 정의 확인 3. `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) - https://github.com/MiraboxSpace/StreamDock-Plugin-SDK ### 관련 문서 - `Implementation_Guide.md` - Streamingle 플러그인 구현 가이드 - `JSON_Protocol_Specification.md` - JSON 통신 프로토콜 명세 --- **작성일**: 2025-01-27 **버전**: 1.0 **작성자**: SystemController 통합 프로젝트