diff --git a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs index 198793b24..274d82d7e 100644 --- a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs +++ b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs @@ -98,6 +98,9 @@ namespace KindRetargeting [Header("어깨 보정")] [SerializeField] public ShoulderCorrectionFunction shoulderCorrection = new ShoulderCorrectionFunction(); + [Header("프랍 부착")] + [SerializeField] public PropLocationController propLocation = new PropLocationController(); + [Header("아바타 크기 조정")] [SerializeField, Range(0.1f, 3f)] private float avatarScale = 1f; private float previousScale = 1f; @@ -325,6 +328,10 @@ namespace KindRetargeting // 어깨 보정 모듈 초기화 if (targetAnimator != null) shoulderCorrection.Initialize(targetAnimator); + + // 프랍 부착 모듈 초기화 + if (targetAnimator != null) + propLocation.Initialize(targetAnimator); } /// diff --git a/Assets/Scripts/KindRetargeting/Editor/PropLocationControllerEditor.cs b/Assets/Scripts/KindRetargeting/Editor/PropLocationControllerEditor.cs deleted file mode 100644 index acb336690..000000000 --- a/Assets/Scripts/KindRetargeting/Editor/PropLocationControllerEditor.cs +++ /dev/null @@ -1,167 +0,0 @@ -using UnityEngine; -using UnityEditor; -using UnityEngine.UIElements; -using UnityEditor.UIElements; - -namespace KindRetargeting -{ - [CustomEditor(typeof(PropLocationController))] - public class PropLocationControllerEditor : BaseRetargetingEditor - { - private const string CommonUssPath = "Assets/Scripts/Streamingle/StreamingleControl/Editor/UXML/StreamingleCommon.uss"; - - private VisualElement offsetContainer; - private VisualElement propListContainer; - private PropLocationController controller; - - public override VisualElement CreateInspectorGUI() - { - controller = target as PropLocationController; - if (controller == null) return new VisualElement(); - var root = new VisualElement(); - - var commonUss = AssetDatabase.LoadAssetAtPath(CommonUssPath); - if (commonUss != null) root.styleSheets.Add(commonUss); - - // 기본 프로퍼티 - var iterator = serializedObject.GetIterator(); - iterator.NextVisible(true); // skip m_Script - while (iterator.NextVisible(false)) - { - var field = new PropertyField(iterator.Copy()); - root.Add(field); - } - - // 오프셋 조정 섹션 - root.Add(new Label("오프셋 조정") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginTop = 10 } }); - offsetContainer = new VisualElement(); - root.Add(offsetContainer); - - // 부착된 프랍 목록 섹션 - root.Add(new Label("부착된 프랍 목록") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginTop = 10 } }); - propListContainer = new VisualElement(); - root.Add(propListContainer); - - // 프랍 위치 이동 버튼 - root.Add(new Label("프랍 위치 이동") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginTop = 10 } }); - var btnRow = new VisualElement { style = { flexDirection = FlexDirection.Row, marginTop = 4 } }; - - var headBtn = new Button(() => controller.MoveToHead()) { text = "머리로 이동" }; - headBtn.style.flexGrow = 1; headBtn.style.height = 30; headBtn.style.marginRight = 2; - btnRow.Add(headBtn); - - var leftBtn = new Button(() => controller.MoveToLeftHand()) { text = "왼손으로 이동" }; - leftBtn.style.flexGrow = 1; leftBtn.style.height = 30; leftBtn.style.marginRight = 2; - btnRow.Add(leftBtn); - - var rightBtn = new Button(() => controller.MoveToRightHand()) { text = "오른손으로 이동" }; - rightBtn.style.flexGrow = 1; rightBtn.style.height = 30; - btnRow.Add(rightBtn); - - root.Add(btnRow); - - var detachBtn = new Button(() => - { - if (Selection.activeGameObject != null) - Undo.RecordObject(Selection.activeGameObject.transform, "Detach Prop"); - controller.DetachProp(); - }) { text = "프랍 해제" }; - detachBtn.style.height = 30; - detachBtn.style.marginTop = 4; - root.Add(detachBtn); - - // 주기적으로 동적 UI 갱신 - root.schedule.Execute(() => RebuildDynamicUI()).Every(500); - RebuildDynamicUI(); - - return root; - } - - private void RebuildDynamicUI() - { - if (controller == null) return; - RebuildOffsets(); - RebuildPropLists(); - } - - private void RebuildOffsets() - { - if (offsetContainer == null) return; - offsetContainer.Clear(); - - BuildOffsetSection(offsetContainer, "왼손 오프셋", controller.GetLeftHandOffset()); - BuildOffsetSection(offsetContainer, "오른손 오프셋", controller.GetRightHandOffset()); - BuildOffsetSection(offsetContainer, "머리 오프셋", controller.GetHeadOffset()); - } - - private void BuildOffsetSection(VisualElement parent, string label, Transform offset) - { - if (offset == null) return; - - var foldout = new Foldout { text = label, value = true }; - - var posField = new Vector3Field("위치") { value = offset.localPosition }; - posField.RegisterValueChangedCallback(evt => - { - Undo.RecordObject(offset, $"Update {label}"); - offset.localPosition = evt.newValue; - EditorUtility.SetDirty(offset); - }); - foldout.Add(posField); - - var rotField = new Vector3Field("회전") { value = offset.localRotation.eulerAngles }; - rotField.RegisterValueChangedCallback(evt => - { - Undo.RecordObject(offset, $"Update {label}"); - offset.localRotation = Quaternion.Euler(evt.newValue); - EditorUtility.SetDirty(offset); - }); - foldout.Add(rotField); - - parent.Add(foldout); - } - - private void RebuildPropLists() - { - if (propListContainer == null) return; - propListContainer.Clear(); - - BuildPropListSection(propListContainer, "머리 프랍", controller.GetHeadProps()); - BuildPropListSection(propListContainer, "왼손 프랍", controller.GetLeftHandProps()); - BuildPropListSection(propListContainer, "오른손 프랍", controller.GetRightHandProps()); - } - - private void BuildPropListSection(VisualElement parent, string title, GameObject[] propList) - { - var box = new VisualElement(); - box.style.backgroundColor = new Color(0, 0, 0, 0.1f); - box.style.borderTopLeftRadius = box.style.borderTopRightRadius = - box.style.borderBottomLeftRadius = box.style.borderBottomRightRadius = 4; - box.style.paddingTop = box.style.paddingBottom = - box.style.paddingLeft = box.style.paddingRight = 4; - box.style.marginBottom = 4; - - box.Add(new Label(title) { style = { unityFontStyleAndWeight = FontStyle.Bold } }); - - if (propList.Length > 0) - { - foreach (var prop in propList) - { - if (prop == null) continue; - var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginTop = 2 } }; - row.Add(new Label(prop.name) { style = { flexGrow = 1 } }); - var selectBtn = new Button(() => Selection.activeGameObject = prop) { text = "선택" }; - selectBtn.style.width = 60; - row.Add(selectBtn); - box.Add(row); - } - } - else - { - box.Add(new Label("부착된 프랍 없음") { style = { color = new Color(0.6f, 0.6f, 0.6f) } }); - } - - parent.Add(box); - } - } -} diff --git a/Assets/Scripts/KindRetargeting/Editor/PropLocationControllerEditor.cs.meta b/Assets/Scripts/KindRetargeting/Editor/PropLocationControllerEditor.cs.meta deleted file mode 100644 index 012ce72ed..000000000 --- a/Assets/Scripts/KindRetargeting/Editor/PropLocationControllerEditor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a49ee1ae55b970e4c8ca00ccae5d6f97 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs index 5c5cf6351..f00dce14a 100644 --- a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs +++ b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs @@ -148,8 +148,7 @@ public class RetargetingControlWindow : EditorWindow s.gameObject.AddComponent(); if (s.GetComponent() == null) s.gameObject.AddComponent(); - if (s.GetComponent() == null) - s.gameObject.AddComponent(); + // PropLocationController는 CRS 내부 모듈로 이동됨 EditorUtility.SetDirty(s.gameObject); } @@ -606,19 +605,7 @@ public class RetargetingControlWindow : EditorWindow private VisualElement BuildPropSection(CustomRetargetingScript script) { var foldout = new Foldout { text = "프랍 설정", value = false }; - var propController = script.GetComponent(); - if (propController == null) - { - foldout.Add(new HelpBox("PropLocationController가 없습니다.", HelpBoxMessageType.Warning)); - var addBtn = new Button(() => - { - script.gameObject.AddComponent(); - EditorUtility.SetDirty(script.gameObject); - RebuildCharacterPanels(); - }) { text = "PropLocationController 추가" }; - foldout.Add(addBtn); - return foldout; - } + var propController = script.propLocation; var dynamicContainer = new VisualElement(); foldout.Add(dynamicContainer); diff --git a/Assets/Scripts/KindRetargeting/PropLocationController.cs b/Assets/Scripts/KindRetargeting/PropLocationController.cs index b6048dfb2..492693eac 100644 --- a/Assets/Scripts/KindRetargeting/PropLocationController.cs +++ b/Assets/Scripts/KindRetargeting/PropLocationController.cs @@ -4,7 +4,8 @@ using UniHumanoid; namespace KindRetargeting { - public class PropLocationController : MonoBehaviour + [System.Serializable] + public class PropLocationController { // 캐시된 타겟과 오프셋 Transform [System.Serializable] @@ -18,9 +19,9 @@ namespace KindRetargeting [SerializeField] private TargetOffset rightHandTargetOffset; [SerializeField] private TargetOffset headTargetOffset; - private void Start() + public void Initialize(Animator animator) { - CreateTargets(); + CreateTargets(animator); } public void SetTPose(Animator animator) @@ -31,7 +32,6 @@ namespace KindRetargeting Avatar avatar = animator.avatar; Transform transform = animator.transform; - // HumanPoseClip에 저장된 T-포즈 데이터를 로드하여 적용 var humanPoseClip = Resources.Load(HumanPoseClip.TPoseResourcePath); if (humanPoseClip != null) { @@ -43,9 +43,9 @@ namespace KindRetargeting Debug.LogWarning("T-Pose 데이터가 존재하지 않습니다."); } } - private void CreateTargets() + + private void CreateTargets(Animator animator) { - Animator animator = GetComponent(); SetTPose(animator); // 왼손 타겟 및 오프셋 설정 @@ -54,7 +54,7 @@ namespace KindRetargeting { leftHandTargetOffset = new TargetOffset(); GameObject leftTarget = new GameObject("Left_Hand_Target"); - leftTarget.transform.parent = leftHandBone; // 왼손 본에 직접 부모 설정 + leftTarget.transform.parent = leftHandBone; leftHandTargetOffset.target = leftTarget.transform; leftTarget.transform.position = leftHandBone.position + new Vector3(-0.039f, -0.022f, 0f); leftTarget.transform.rotation = Quaternion.Euler(90f, 0f, 0f); @@ -63,7 +63,6 @@ namespace KindRetargeting leftOffset.transform.parent = leftTarget.transform; leftHandTargetOffset.offset = leftOffset.transform; - // 로컬 포지션과 로테이션 설정 leftHandTargetOffset.offset.localPosition = Vector3.zero; leftHandTargetOffset.offset.localRotation = Quaternion.identity; } @@ -74,7 +73,7 @@ namespace KindRetargeting { rightHandTargetOffset = new TargetOffset(); GameObject rightTarget = new GameObject("Right_Hand_Target"); - rightTarget.transform.parent = rightHandBone; // 오른손 본에 직접 부모 설정 + rightTarget.transform.parent = rightHandBone; rightHandTargetOffset.target = rightTarget.transform; rightTarget.transform.position = rightHandBone.position + new Vector3(0.039f, -0.022f, 0f); rightTarget.transform.rotation = Quaternion.Euler(90f, 0f, 0f); @@ -83,7 +82,6 @@ namespace KindRetargeting rightOffset.transform.parent = rightTarget.transform; rightHandTargetOffset.offset = rightOffset.transform; - // 로컬 포지션과 로테이션 설정 rightHandTargetOffset.offset.localPosition = Vector3.zero; rightHandTargetOffset.offset.localRotation = Quaternion.identity; } @@ -94,7 +92,7 @@ namespace KindRetargeting { headTargetOffset = new TargetOffset(); GameObject headTarget = new GameObject("Head_Target"); - headTarget.transform.parent = headBone; // 머리 본에 직접 부모 설정 + headTarget.transform.parent = headBone; headTargetOffset.target = headTarget.transform; headTarget.transform.position = headBone.position + new Vector3(0f, 0.16f, 0f); headTarget.transform.rotation = Quaternion.Euler(0f, 0f, 0f); @@ -103,7 +101,6 @@ namespace KindRetargeting headOffset.transform.parent = headTarget.transform; headTargetOffset.offset = headOffset.transform; - // 기본 오프셋 설정 headTargetOffset.offset.localPosition = Vector3.zero; headTargetOffset.offset.localRotation = Quaternion.identity; } @@ -172,7 +169,6 @@ namespace KindRetargeting } } - // 에디터에서 사용할 메서드들 #if UNITY_EDITOR public void MoveToHead() { @@ -195,7 +191,6 @@ namespace KindRetargeting } #endif - // 오프셋 getter 메서드들 추가 public Transform GetLeftHandOffset() { return leftHandTargetOffset?.offset; @@ -239,4 +234,4 @@ namespace KindRetargeting return children.ToArray(); } } -} \ No newline at end of file +}