Compare commits

...

2 Commits

Author SHA1 Message Date
c6e4ac7962 Merge branch 'main' of https://kindnick-git.duckdns.org/kindnick/Streamingle_URP 2026-01-22 22:50:52 +09:00
34564f0f50 ADD: MagicaCloth 시뮬레이션 전체 리프레시 기능 추가
SystemController에 MagicaCloth2 시뮬레이션 리셋 기능 구현:
- RefreshAllMagicaCloth(): 씬의 모든 MagicaCloth 시뮬레이션 리셋
- ResetAllMagicaCloth(): 초기 상태로 완전 리셋
- ResetAllMagicaClothKeepPose(): 현재 포즈 유지하며 리셋
- #if MAGICACLOTH2 조건부 컴파일로 의존성 처리

StreamDeck 플러그인 연동:
- manifest.json에 MagicaCloth Refresh 액션 추가
- plugin/index.js에 버튼 핸들러 구현
- StreamDeckServerManager에 메시지 타입 등록
- magica_cloth_reload.png 아이콘 추가

WebSocket 명령어:
- refresh_magica_cloth, reset_magica_cloth: 전체 리셋
- reset_magica_cloth_keep_pose: 포즈 유지 리셋

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 22:50:04 +09:00
5 changed files with 158 additions and 2 deletions

View File

@ -357,6 +357,9 @@ public class StreamDeckServerManager : MonoBehaviour
case "capture_screenshot": case "capture_screenshot":
case "capture_alpha_screenshot": case "capture_alpha_screenshot":
case "open_screenshot_folder": case "open_screenshot_folder":
case "refresh_magica_cloth":
case "reset_magica_cloth":
case "reset_magica_cloth_keep_pose":
HandleSystemCommand(message); HandleSystemCommand(message);
break; break;

View File

@ -4,6 +4,9 @@ using System.Linq;
using System.IO; using System.IO;
using System; using System;
using Entum; using Entum;
#if MAGICACLOTH2
using MagicaCloth2;
#endif
/// <summary> /// <summary>
/// StreamDeck 단일 기능 버튼들을 통합 관리하는 시스템 컨트롤러 /// StreamDeck 단일 기능 버튼들을 통합 관리하는 시스템 컨트롤러
@ -681,6 +684,77 @@ public class SystemController : MonoBehaviour
#endregion #endregion
#region MagicaCloth
#if MAGICACLOTH2
/// <summary>
/// 씬의 모든 MagicaCloth 시뮬레이션을 리셋합니다
/// </summary>
/// <param name="keepPose">true면 현재 포즈를 유지하면서 리셋</param>
public void RefreshAllMagicaCloth(bool keepPose = false)
{
var allMagicaCloths = FindObjectsByType<MagicaCloth>(FindObjectsSortMode.None);
if (allMagicaCloths == null || allMagicaCloths.Length == 0)
{
Log("씬에 MagicaCloth 컴포넌트가 없습니다.");
return;
}
int resetCount = 0;
foreach (var cloth in allMagicaCloths)
{
if (cloth != null && cloth.IsValid())
{
try
{
cloth.ResetCloth(keepPose);
resetCount++;
}
catch (System.Exception e)
{
LogError($"MagicaCloth 리셋 실패 ({cloth.gameObject.name}): {e.Message}");
}
}
}
Log($"MagicaCloth 시뮬레이션 리셋 완료 ({resetCount}/{allMagicaCloths.Length}개)");
}
/// <summary>
/// 씬의 모든 MagicaCloth 시뮬레이션을 완전히 초기 상태로 리셋합니다
/// </summary>
public void ResetAllMagicaCloth()
{
RefreshAllMagicaCloth(false);
}
/// <summary>
/// 씬의 모든 MagicaCloth 시뮬레이션을 현재 포즈를 유지하면서 리셋합니다
/// </summary>
public void ResetAllMagicaClothKeepPose()
{
RefreshAllMagicaCloth(true);
}
#else
public void RefreshAllMagicaCloth(bool keepPose = false)
{
LogError("MagicaCloth2가 설치되어 있지 않습니다.");
}
public void ResetAllMagicaCloth()
{
LogError("MagicaCloth2가 설치되어 있지 않습니다.");
}
public void ResetAllMagicaClothKeepPose()
{
LogError("MagicaCloth2가 설치되어 있지 않습니다.");
}
#endif
#endregion
/// <summary> /// <summary>
/// 명령어 실행 - WebSocket에서 받은 명령을 처리 /// 명령어 실행 - WebSocket에서 받은 명령을 처리
/// </summary> /// </summary>
@ -747,6 +821,16 @@ public class SystemController : MonoBehaviour
OpenScreenshotFolder(); OpenScreenshotFolder();
break; break;
// MagicaCloth 시뮬레이션
case "refresh_magica_cloth":
case "reset_magica_cloth":
ResetAllMagicaCloth();
break;
case "reset_magica_cloth_keep_pose":
ResetAllMagicaClothKeepPose();
break;
default: default:
LogError($"알 수 없는 명령어: {command}"); LogError($"알 수 없는 명령어: {command}");
break; break;

Binary file not shown.

Binary file not shown.

View File

@ -147,6 +147,20 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction
newSettings.actionType = 'screenshot_alpha'; newSettings.actionType = 'screenshot_alpha';
console.log('📷 알파 스크린샷 버튼으로 강제 설정됨'); console.log('📷 알파 스크린샷 버튼으로 강제 설정됨');
// StreamDock SDK에 저장
if (websocket) {
const setSettingsMessage = {
event: 'setSettings',
context: jsonObj.context,
payload: newSettings
};
websocket.send(JSON.stringify(setSettingsMessage));
console.log('💾 강제 설정 저장:', newSettings);
}
} else if (jsonObj.action === 'com.mirabox.streamingle.magica_cloth_refresh') {
newSettings.actionType = 'magica_cloth_refresh';
console.log('👗 MagicaCloth 리프레시 버튼으로 강제 설정됨');
// StreamDock SDK에 저장 // StreamDock SDK에 저장
if (websocket) { if (websocket) {
const setSettingsMessage = { const setSettingsMessage = {
@ -240,6 +254,14 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction
// 기본 제목 설정 // 기본 제목 설정
setButtonTitle(jsonObj.context, '알파\n스크린샷'); setButtonTitle(jsonObj.context, '알파\n스크린샷');
} else if (jsonObj.action === 'com.mirabox.streamingle.magica_cloth_refresh') {
settings.actionType = 'magica_cloth_refresh';
console.log('👗👗👗 MAGICA CLOTH REFRESH BUTTON DETECTED 👗👗👗');
console.log('👗 This is the MagicaCloth Refresh button!');
console.log('👗 When clicked, it should send: refresh_magica_cloth');
// 기본 제목 설정
setButtonTitle(jsonObj.context, '마지카\n리프레시');
} else { } else {
settings.actionType = 'camera'; settings.actionType = 'camera';
console.log('📹 카메라 컨트롤러 등록 (기본값)'); console.log('📹 카메라 컨트롤러 등록 (기본값)');
@ -486,6 +508,9 @@ function handleButtonClick(context, actionUUID) {
} else if (actionUUID === 'com.mirabox.streamingle.screenshot_alpha') { } else if (actionUUID === 'com.mirabox.streamingle.screenshot_alpha') {
actionType = 'screenshot_alpha'; actionType = 'screenshot_alpha';
console.log('📷 알파 스크린샷 버튼으로 인식'); console.log('📷 알파 스크린샷 버튼으로 인식');
} else if (actionUUID === 'com.mirabox.streamingle.magica_cloth_refresh') {
actionType = 'magica_cloth_refresh';
console.log('👗 MagicaCloth 리프레시 버튼으로 인식');
} else if (actionUUID === 'com.mirabox.streamingle.item') { } else if (actionUUID === 'com.mirabox.streamingle.item') {
actionType = 'item'; actionType = 'item';
} else if (actionUUID === 'com.mirabox.streamingle.event') { } else if (actionUUID === 'com.mirabox.streamingle.event') {
@ -562,6 +587,11 @@ function handleButtonClick(context, actionUUID) {
console.log(' Will send: {"type":"capture_alpha_screenshot"}'); console.log(' Will send: {"type":"capture_alpha_screenshot"}');
handleAlphaScreenshot(context); handleAlphaScreenshot(context);
break; break;
case 'magica_cloth_refresh':
console.log('➡️ Routing to: MAGICA CLOTH REFRESH handler');
console.log(' Will send: {"type":"refresh_magica_cloth"}');
handleMagicaClothRefresh(context);
break;
default: default:
console.log('⚠️ WARNING: Unknown actionType:', actionType); console.log('⚠️ WARNING: Unknown actionType:', actionType);
console.log(' Defaulting to CAMERA handler'); console.log(' Defaulting to CAMERA handler');
@ -962,6 +992,37 @@ function handleAlphaScreenshot(context) {
} }
} }
// MagicaCloth 리프레시 액션 처리
function handleMagicaClothRefresh(context) {
console.log('👗 MagicaCloth 시뮬레이션 리프레시 실행');
// Unity에 MagicaCloth 리프레시 요청
const message = JSON.stringify({
type: 'refresh_magica_cloth'
});
console.log('📤 Unity에 MagicaCloth 리프레시 요청 전송:', message);
console.log('🔍 Unity 연결 상태:', isUnityConnected);
console.log('🔍 Unity 소켓 상태:', !!unitySocket);
if (unitySocket && unitySocket.readyState === WebSocket.OPEN) {
unitySocket.send(message);
console.log('✅ 메시지 전송 완료');
// 피드백을 위해 제목을 잠시 변경
setButtonTitle(context, '리셋\n중...');
// 1초 후 원래 제목으로 복구
setTimeout(() => {
setButtonTitle(context, '마지카\n리프레시');
}, 1000);
} else {
console.error('❌ Unity 소켓이 연결되지 않음');
console.log('🔄 Unity 재연결 시도...');
connectToUnity();
}
}
// Property Inspector 메시지 처리 // Property Inspector 메시지 처리
function handlePropertyInspectorMessage(payload, context, actionUUID) { function handlePropertyInspectorMessage(payload, context, actionUUID) {
const command = payload.command; const command = payload.command;
@ -1947,6 +2008,11 @@ function updateButtonTitle(context) {
title = '알파\n스크린샷'; title = '알파\n스크린샷';
isActive = true; // 항상 활성 상태 isActive = true; // 항상 활성 상태
console.log('📷 알파 스크린샷 버튼 제목:', title); console.log('📷 알파 스크린샷 버튼 제목:', title);
} else if (actionType === 'magica_cloth_refresh') {
// MagicaCloth 리프레시 버튼
title = '마지카\n리프레시';
isActive = true; // 항상 활성 상태
console.log('👗 MagicaCloth 리프레시 버튼 제목:', title);
} }
// StreamDock에 제목 업데이트 요청 // StreamDock에 제목 업데이트 요청